Merge pull request #18511 from hrydgard/screen-manager-refactor

Screen manager refactoring, preparation for running the game behind the pause screen
This commit is contained in:
Henrik Rydgård 2023-12-11 08:50:08 +01:00 committed by GitHub
commit 9f01661774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 298 additions and 243 deletions

View File

@ -291,7 +291,7 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
_dbg_assert_(!(isBackbuffer && multisample));
if (isBackbuffer) {
_dbg_assert_(key.depthLoadAction == VKRRenderPassLoadAction::CLEAR);
_dbg_assert_(key.depthLoadAction != VKRRenderPassLoadAction::KEEP);
}
if (multiview) {

View File

@ -224,6 +224,7 @@ enum class UIMessage {
REQUEST_GAME_PAUSE,
REQUEST_GAME_RESET,
REQUEST_GAME_STOP,
GAME_SELECTED,
SHOW_CONTROL_MAPPING,
SHOW_CHAT_SCREEN,
SHOW_DISPLAY_LAYOUT_EDITOR,

View File

@ -164,8 +164,7 @@ void UIContext::ActivateTopScissor() {
int w = std::max(0.0f, ceilf(scale_x * bounds.w));
int h = std::max(0.0f, ceilf(scale_y * bounds.h));
if (x < 0 || y < 0 || x + w > g_display.pixel_xres || y + h > g_display.pixel_yres) {
// This won't actually report outside a game, but we can try.
DEBUG_LOG(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, g_display.pixel_xres, g_display.pixel_yres);
DEBUG_LOG(G3D, "UI scissor out of bounds: %d,%d-%d,%d / %d,%d", x, y, w, h, g_display.pixel_xres, g_display.pixel_yres);
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x >= g_display.pixel_xres) { x = g_display.pixel_xres - 1; }

View File

@ -113,10 +113,6 @@ public:
void setUIAtlas(const std::string &name);
void SetScreenTag(const char *tag) {
screenTag_ = tag;
}
// TODO: Move to private.
const UI::Theme *theme;
@ -142,6 +138,4 @@ private:
std::string lastUIAtlas_;
std::string UIAtlas_ = "ui_atlas.zim";
const char *screenTag_ = nullptr;
};

View File

@ -6,7 +6,6 @@
#include "Common/UI/UI.h"
#include "Common/UI/View.h"
#include "Common/UI/ViewGroup.h"
#include "Common/UI/IconCache.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
@ -61,11 +60,10 @@ void ScreenManager::update() {
// NOTE: This is not a full UIScreen update, to avoid double global event processing.
overlayScreen_->update();
}
// The background screen doesn't need updating.
if (stack_.size()) {
stack_.back().screen->update();
}
g_iconCache.FrameUpdate();
}
void ScreenManager::switchToNext() {
@ -139,7 +137,6 @@ void ScreenManager::axis(const AxisInput *axes, size_t count) {
void ScreenManager::deviceLost() {
for (auto &iter : stack_)
iter.screen->deviceLost();
g_iconCache.ClearTextures();
}
void ScreenManager::deviceRestored() {
@ -159,52 +156,68 @@ void ScreenManager::resized() {
void ScreenManager::render() {
if (!stack_.empty()) {
switch (stack_.back().flags) {
case LAYER_TRANSPARENT:
if (stack_.size() == 1) {
ERROR_LOG(SYSTEM, "Can't have sidemenu over nothing");
break;
} else {
auto last = stack_.end();
auto iter = last;
iter--;
while (iter->flags == LAYER_TRANSPARENT) {
iter--;
}
auto first = iter;
_assert_(iter->screen);
// Collect the screens to render
TinySet<Screen *, 6> layers;
// TODO: Make really sure that this "mismatched" pre/post only happens
// when screens are "compatible" (both are UIScreens, for example).
first->screen->preRender();
while (iter < last) {
iter->screen->render();
iter++;
}
stack_.back().screen->render();
if (overlayScreen_) {
overlayScreen_->render();
}
if (postRenderCb_) {
// Really can't render anything after this! Will crash the screenshot mechanism if we do.
postRenderCb_(getUIContext(), postRenderUserdata_);
}
first->screen->postRender();
break;
// Start at the end, collect screens to form the transparency stack.
// Then we'll iterate them in reverse order.
// Note that we skip the overlay screen, we handle it separately.
// Additionally, we pick up a "background" screen. Normally it will be either
// the EmuScreen or the actual global background screen.
auto iter = stack_.end();
Screen *coveringScreen = nullptr;
Screen *backgroundScreen = nullptr;
do {
--iter;
if (!coveringScreen) {
layers.push_back(iter->screen);
} else if (!backgroundScreen && iter->screen->canBeBackground()) {
// There still might be a screen that wants to be background - generally the EmuScreen if present.
layers.push_back(iter->screen);
backgroundScreen = iter->screen;
}
default:
_assert_(stack_.back().screen);
stack_.back().screen->preRender();
stack_.back().screen->render();
if (overlayScreen_) {
overlayScreen_->render();
if (iter->flags != LAYER_TRANSPARENT) {
coveringScreen = iter->screen;
}
if (postRenderCb_) {
// Really can't render anything after this! Will crash the screenshot mechanism if we do.
postRenderCb_(getUIContext(), postRenderUserdata_);
} while (iter != stack_.begin());
// Confusing-looking expression, argh! Note the '_'
if (backgroundScreen_ && !backgroundScreen) {
layers.push_back(backgroundScreen_);
backgroundScreen = backgroundScreen_;
}
// OK, now we iterate backwards over our little pile of collected screens.
bool first = true;
for (int i = (int)layers.size() - 1; i >= 0; i--) {
ScreenRenderMode mode = ScreenRenderMode::DEFAULT;
if (i == (int)layers.size() - 1) {
// Bottom.
mode = ScreenRenderMode::FIRST;
if (layers[i] == backgroundScreen && coveringScreen != layers[i]) {
mode |= ScreenRenderMode::BACKGROUND;
}
if (i == 0) {
mode |= ScreenRenderMode::TOP;
}
} else if (i == 0) {
mode = ScreenRenderMode::TOP;
} else {
mode = ScreenRenderMode::BEHIND;
}
stack_.back().screen->postRender();
break;
layers[i]->render(mode);
}
if (overlayScreen_) {
// It doesn't care about mode.
overlayScreen_->render(ScreenRenderMode::TOP);
}
getUIContext()->Flush();
if (postRenderCb_) {
// Really can't render anything after this! Will crash the screenshot mechanism if we do.
postRenderCb_(getUIContext(), postRenderUserdata_);
}
} else {
ERROR_LOG(SYSTEM, "No current screen!");
@ -235,8 +248,13 @@ void ScreenManager::sendMessage(UIMessage message, const char *value) {
touch(input);
}
if (!stack_.empty())
if (backgroundScreen_) {
backgroundScreen_->sendMessage(message, value);
}
if (!stack_.empty()) {
stack_.back().screen->sendMessage(message, value);
}
}
Screen *ScreenManager::topScreen() const {
@ -370,10 +388,16 @@ void ScreenManager::processFinishDialog() {
}
}
void ScreenManager::SetOverlayScreen(Screen *screen) {
void ScreenManager::SetBackgroundOverlayScreens(Screen *backgroundScreen, Screen *overlayScreen) {
if (backgroundScreen_) {
delete backgroundScreen_;
}
backgroundScreen_ = backgroundScreen;
backgroundScreen_->setScreenManager(this);
if (overlayScreen_) {
delete overlayScreen_;
}
overlayScreen_ = screen;
overlayScreen_ = overlayScreen;
overlayScreen_->setScreenManager(this);
}

View File

@ -47,6 +47,15 @@ enum class ScreenFocusChange {
FOCUS_BECAME_TOP, // Became the top screen again
};
enum class ScreenRenderMode {
DEFAULT = 0,
FIRST = 1,
BACKGROUND = 2,
BEHIND = 4,
TOP = 8,
};
ENUM_CLASS_BITOPS(ScreenRenderMode);
class Screen {
public:
Screen() : screenManager_(nullptr) { }
@ -56,14 +65,14 @@ public:
virtual void onFinish(DialogResult reason) {}
virtual void update() {}
virtual void preRender() {}
virtual void render() {}
virtual void postRender() {}
virtual void render(ScreenRenderMode mode) {}
virtual void resized() {}
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
virtual void sendMessage(UIMessage message, const char *value) {}
virtual void deviceLost() {}
virtual void deviceRestored() {}
virtual bool canBeBackground() const { return false; }
virtual bool wantBrightBackground() const { return false; } // special hack for DisplayLayoutScreen.
virtual void focusChanged(ScreenFocusChange focusChange);
@ -78,10 +87,6 @@ public:
ScreenManager *screenManager() { return screenManager_; }
void setScreenManager(ScreenManager *sm) { screenManager_ = sm; }
// This one is icky to use because you can't know what's in it until you know
// what screen it is.
virtual void *dialogData() { return 0; }
virtual const char *tag() const = 0;
virtual bool isTransparent() const { return false; }
@ -153,7 +158,7 @@ public:
void getFocusPosition(float &x, float &y, float &z);
// Will delete any existing overlay screen.
void SetOverlayScreen(Screen *screen);
void SetBackgroundOverlayScreens(Screen *backgroundScreen, Screen *overlayScreen);
std::recursive_mutex inputLock_;
@ -171,6 +176,7 @@ private:
const Screen *dialogFinished_ = nullptr;
DialogResult dialogResult_{};
Screen *backgroundScreen_ = nullptr;
Screen *overlayScreen_ = nullptr;
struct Layer {

View File

@ -193,7 +193,7 @@ void UIScreen::deviceRestored() {
root_->DeviceRestored(screenManager()->getDrawContext());
}
void UIScreen::preRender() {
void UIScreen::SetupViewport() {
using namespace Draw;
Draw::DrawContext *draw = screenManager()->getDrawContext();
_dbg_assert_(draw != nullptr);
@ -212,28 +212,26 @@ void UIScreen::preRender() {
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}
void UIScreen::postRender() {
screenManager()->getUIContext()->Flush();
}
void UIScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
SetupViewport();
}
void UIScreen::render() {
DoRecreateViews();
if (root_) {
UIContext *uiContext = screenManager()->getUIContext();
UIContext &uiContext = *screenManager()->getUIContext();
uiContext->SetScreenTag(tag());
UI::LayoutViewHierarchy(uiContext, root_, ignoreInsets_);
UI::LayoutViewHierarchy(*uiContext, root_, ignoreInsets_);
uiContext.PushTransform({translation_, scale_, alpha_});
uiContext->PushTransform({translation_, scale_, alpha_});
uiContext.Begin();
DrawBackground(uiContext);
root_->Draw(uiContext);
uiContext.Flush();
uiContext->Begin();
DrawBackground(*uiContext);
root_->Draw(*uiContext);
uiContext->Flush();
uiContext->PopTransform();
uiContext.PopTransform();
}
}

View File

@ -36,9 +36,7 @@ public:
~UIScreen();
void update() override;
void preRender() override;
void render() override;
void postRender() override;
void render(ScreenRenderMode mode) override;
void deviceLost() override;
void deviceRestored() override;
@ -61,7 +59,6 @@ public:
protected:
virtual void CreateViews() = 0;
virtual void DrawBackground(UIContext &dc) {}
void RecreateViews() override { recreateViews_ = true; }
bool UseVerticalLayout() const;
@ -74,6 +71,8 @@ protected:
bool ignoreInput_ = false;
protected:
virtual void DrawBackground(UIContext &ui) {}
void SetupViewport();
void DoRecreateViews();
bool recreateViews_ = true;
@ -111,6 +110,9 @@ public:
void SetHasDropShadow(bool has) { hasDropShadow_ = has; }
// For the postproc param sliders on DisplayLayoutScreen
bool wantBrightBackground() const override { return !hasDropShadow_; }
protected:
virtual bool FillVertical() const { return false; }
virtual UI::Size PopupWidth() const { return 550; }

View File

@ -713,8 +713,8 @@ void TouchTestScreen::axis(const AxisInput &axis) {
UpdateLogView();
}
void TouchTestScreen::render() {
UIDialogScreenWithGameBackground::render();
void TouchTestScreen::render(ScreenRenderMode mode) {
UIDialogScreenWithGameBackground::render(mode);
UIContext *ui_context = screenManager()->getUIContext();
Bounds bounds = ui_context->GetLayoutBounds();

View File

@ -147,7 +147,7 @@ public:
}
void touch(const TouchInput &touch) override;
void render() override;
void render(ScreenRenderMode mode) override;
bool key(const KeyInput &key) override;
void axis(const AxisInput &axis) override;

View File

@ -117,19 +117,15 @@ private:
float startDisplayOffsetY_ = -1.0f;
};
DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {
// Show background at full brightness
darkenGameBackground_ = false;
forceTransparent_ = true;
}
DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {}
void DisplayLayoutScreen::DrawBackground(UIContext &dc) {
if (PSP_IsInited() && !g_Config.bSkipBufferEffects) {
// We normally rely on the PSP screen.
UIDialogScreenWithGameBackground::DrawBackground(dc);
// We normally rely on the PSP screen showing through.
} else {
// But if it's not present (we're not in game, or skip buffer effects is used),
// we have to draw a substitute ourselves.
UIContext &dc = *screenManager()->getUIContext();
// TODO: Clean this up a bit, this GetScreenFrame/CenterDisplay combo is too common.
FRect screenFrame = GetScreenFrame(g_display.pixel_xres, g_display.pixel_yres);
@ -138,6 +134,7 @@ void DisplayLayoutScreen::DrawBackground(UIContext &dc) {
dc.Flush();
ImageID bg = ImageID("I_PSP_DISPLAY");
dc.Draw()->DrawImageStretch(bg, dc.GetBounds(), 0x7F000000);
dc.Draw()->DrawImageStretch(bg, FRectToBounds(rc), 0x7FFFFFFF);
}
}

View File

@ -32,18 +32,19 @@ public:
void dialogFinished(const Screen *dialog, DialogResult result) override;
void onFinish(DialogResult reason) override;
void DrawBackground(UIContext &dc) override;
void resized() override {
RecreateViews();
}
bool wantBrightBackground() const override { return true; }
const char *tag() const override { return "DisplayLayout"; }
protected:
UI::EventReturn OnPostProcShaderChange(UI::EventParams &e);
void sendMessage(UIMessage message, const char *value) override;
void DrawBackground(UIContext &dc) override;
private:
UI::ChoiceStrip *mode_ = nullptr;

View File

@ -361,6 +361,10 @@ void EmuScreen::bootGame(const Path &filename) {
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);
screenManager()->getDrawContext()->ResetStats();
if (bootPending_) {
System_PostUIMessage(UIMessage::GAME_SELECTED, filename.c_str());
}
}
void EmuScreen::bootComplete() {
@ -424,6 +428,8 @@ EmuScreen::~EmuScreen() {
PSP_Shutdown();
}
System_PostUIMessage(UIMessage::GAME_SELECTED, "");
g_OSD.ClearAchievementStuff();
SetExtraAssertInfo(nullptr);
@ -1409,45 +1415,62 @@ static void DrawFPS(UIContext *ctx, const Bounds &bounds) {
ctx->RebindTexture();
}
void EmuScreen::preRender() {
using namespace Draw;
DrawContext *draw = screenManager()->getDrawContext();
// Here we do NOT bind the backbuffer or clear the screen, unless non-buffered.
// The emuscreen is different than the others - we really want to allow the game to render to framebuffers
// before we ever bind the backbuffer for rendering. On mobile GPUs, switching back and forth between render
// targets is a mortal sin so it's very important that we don't bind the backbuffer unnecessarily here.
// We only bind it in FramebufferManager::CopyDisplayToOutput (unless non-buffered)...
// We do, however, start the frame in other ways.
bool EmuScreen::canBeBackground() const {
if (g_Config.bSkipBufferEffects)
return false;
if ((g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
// We need to clear here already so that drawing during the frame is done on a clean slate.
if (Core_IsStepping() && gpuStats.numFlips != 0) {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
} else {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
}
bool forceTransparent = false; // this needs to be true somehow on the display layout screen.
Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = g_display.pixel_xres;
viewport.Height = g_display.pixel_yres;
viewport.MaxDepth = 1.0;
viewport.MinDepth = 0.0;
draw->SetViewport(viewport);
if (!g_Config.bTransparentBackground && !forceTransparent)
return false;
return true;
}
void EmuScreen::darken() {
if (!screenManager()->topScreen()->wantBrightBackground()) {
UIContext &dc = *screenManager()->getUIContext();
uint32_t color = GetBackgroundColorWithAlpha(dc);
dc.Begin();
dc.RebindTexture();
dc.FillRect(UI::Drawable(color), dc.GetBounds());
dc.Flush();
}
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}
void EmuScreen::postRender() {
Draw::DrawContext *draw = screenManager()->getDrawContext();
if (!draw)
return;
if (stopRender_)
draw->WipeQueue();
}
void EmuScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
// Actually, always gonna be first when it exists (?)
using namespace Draw;
DrawContext *draw = screenManager()->getDrawContext();
// Here we do NOT bind the backbuffer or clear the screen, unless non-buffered.
// The emuscreen is different than the others - we really want to allow the game to render to framebuffers
// before we ever bind the backbuffer for rendering. On mobile GPUs, switching back and forth between render
// targets is a mortal sin so it's very important that we don't bind the backbuffer unnecessarily here.
// We only bind it in FramebufferManager::CopyDisplayToOutput (unless non-buffered)...
// We do, however, start the frame in other ways.
if ((g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
// We need to clear here already so that drawing during the frame is done on a clean slate.
if (Core_IsStepping() && gpuStats.numFlips != 0) {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
} else {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
}
Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = g_display.pixel_xres;
viewport.Height = g_display.pixel_yres;
viewport.MaxDepth = 1.0;
viewport.MinDepth = 0.0;
draw->SetViewport(viewport);
}
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}
void EmuScreen::render() {
using namespace Draw;
DrawContext *thin3d = screenManager()->getDrawContext();
@ -1456,8 +1479,19 @@ void EmuScreen::render() {
g_OSD.NudgeSidebar();
if (screenManager()->topScreen() == this) {
if (mode & ScreenRenderMode::TOP) {
System_Notify(SystemNotification::KEEP_SCREEN_AWAKE);
} else {
// Not on top. Let's not execute, only draw the image.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
// Just to make sure.
if (PSP_IsInited() && !g_Config.bSkipBufferEffects) {
PSP_BeginHostFrame();
gpu->CopyDisplayToOutput(true);
PSP_EndHostFrame();
darken();
}
return;
}
if (invalid_) {
@ -1552,6 +1586,14 @@ void EmuScreen::render() {
} else {
SetVRAppMode(screenManager()->topScreen() == this ? VRAppMode::VR_GAME_MODE : VRAppMode::VR_DIALOG_MODE);
}
if (mode & ScreenRenderMode::TOP) {
// TODO: Replace this with something else.
if (stopRender_)
thin3d->WipeQueue();
} else if (!screenManager()->topScreen()->wantBrightBackground()) {
darken();
}
}
bool EmuScreen::hasVisibleUI() {

View File

@ -42,12 +42,11 @@ public:
const char *tag() const override { return "Emu"; }
void update() override;
void render() override;
void preRender() override;
void postRender() override;
void render(ScreenRenderMode mode) override;
void dialogFinished(const Screen *dialog, DialogResult result) override;
void sendMessage(UIMessage message, const char *value) override;
void resized() override;
bool canBeBackground() const override;
// Note: Unlike your average boring UIScreen, here we override the Unsync* functions
// to get minimal latency and full control. We forward to UIScreen when needed.
@ -59,7 +58,7 @@ public:
bool key(const KeyInput &key) override;
protected:
void darken();
void focusChanged(ScreenFocusChange focusChange) override;
private:

View File

@ -630,9 +630,9 @@ void GPUDriverTestScreen::ShaderTest() {
}
void GPUDriverTestScreen::render() {
void GPUDriverTestScreen::render(ScreenRenderMode mode) {
using namespace Draw;
UIScreen::render();
UIScreen::render(mode);
switch (tabHolder_->GetCurrentTab()) {
case 0:

View File

@ -15,7 +15,7 @@ public:
~GPUDriverTestScreen();
void CreateViews() override;
void render() override;
void render(ScreenRenderMode mode) override;
const char *tag() const override { return "GPUDriverTest"; }

View File

@ -51,12 +51,14 @@
GameScreen::GameScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) {
g_BackgroundAudio.SetGame(gamePath);
System_PostUIMessage(UIMessage::GAME_SELECTED, gamePath.ToString());
}
GameScreen::~GameScreen() {
if (CRC32string == "...") {
Reporting::CancelCRC();
}
System_PostUIMessage(UIMessage::GAME_SELECTED, "");
}
template <typename I> std::string int2hexstr(I w, size_t hex_len = sizeof(I) << 1) {
@ -274,8 +276,8 @@ UI::EventReturn GameScreen::OnDeleteConfig(UI::EventParams &e)
return UI::EVENT_DONE;
}
void GameScreen::render() {
UIScreen::render();
void GameScreen::render(ScreenRenderMode mode) {
UIScreen::render(mode);
auto ga = GetI18NCategory(I18NCat::GAME);

View File

@ -38,7 +38,7 @@ public:
void update() override;
void render() override;
void render(ScreenRenderMode mode) override;
const char *tag() const override { return "Game"; }

View File

@ -327,6 +327,8 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings)
int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions));
UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, I18NCat::GRAPHICS, screenManager()));
hwscale->OnChoice.Add([](UI::EventParams &) {
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
System_PostUIMessage(UIMessage::GPU_DISPLAY_RESIZED);
System_RecreateActivity();
return UI::EVENT_DONE;
});

View File

@ -1389,7 +1389,6 @@ UI::EventReturn MainScreen::OnFullScreenToggle(UI::EventParams &e) {
}
void MainScreen::DrawBackground(UIContext &dc) {
UIScreenWithBackground::DrawBackground(dc);
if (highlightedGamePath_.empty() && prevHighlightedGamePath_.empty()) {
return;
}

View File

@ -52,11 +52,11 @@ protected:
void dialogFinished(const Screen *dialog, DialogResult result) override;
void update() override;
void render() override {
void render(ScreenRenderMode mode) override {
// Simple anti-flicker due to delayed finish.
if (!done_) {
// render as usual.
UIDialogScreenWithBackground::render();
UIDialogScreenWithBackground::render(mode);
} else {
// no render. black frame insertion is better than flicker.
}

View File

@ -368,36 +368,10 @@ uint32_t GetBackgroundColorWithAlpha(const UIContext &dc) {
return colorAlpha(colorBlend(dc.GetTheme().backgroundColor, 0, 0.5f), 0.65f); // 0.65 = 166 = A6
}
void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z, bool transparent, bool darkenBackground) {
void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z) {
using namespace Draw;
using namespace UI;
if (transparent && PSP_IsInited() && !g_Config.bSkipBufferEffects) {
gpu->CheckDisplayResized();
gpu->CheckConfigChanged();
gpu->CopyDisplayToOutput(true);
DrawContext *draw = dc.GetDrawContext();
Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = g_display.pixel_xres;
viewport.Height = g_display.pixel_yres;
viewport.MaxDepth = 1.0;
viewport.MinDepth = 0.0;
draw->SetViewport(viewport);
dc.BeginFrame();
dc.RebindTexture();
dc.Begin();
if (darkenBackground) {
uint32_t color = GetBackgroundColorWithAlpha(dc);
dc.FillRect(UI::Drawable(color), dc.GetBounds());
dc.Flush();
}
return;
}
std::shared_ptr<GameInfo> ginfo;
if (!gamePath.empty())
ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG);
@ -455,21 +429,43 @@ void HandleCommonMessages(UIMessage message, const char *value, ScreenManager *m
}
}
void UIScreenWithBackground::DrawBackground(UIContext &dc) {
void BackgroundScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
SetupViewport();
} else {
_dbg_assert_(false);
}
UIContext *uiContext = screenManager()->getUIContext();
uiContext->PushTransform({ translation_, scale_, alpha_ });
uiContext->Begin();
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
if (!gamePath_.empty()) {
::DrawGameBackground(*uiContext, gamePath_, x, y, z);
} else {
::DrawBackground(*uiContext, 1.0f, x, y, z);
}
uiContext->Flush();
uiContext->PopTransform();
}
void UIScreenWithGameBackground::DrawBackground(UIContext &dc) {
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
if (!gamePath_.empty()) {
DrawGameBackground(dc, gamePath_, x, y, z, (g_Config.bTransparentBackground || forceTransparent_), darkenGameBackground_);
} else {
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
void BackgroundScreen::sendMessage(UIMessage message, const char *value) {
switch (message) {
case UIMessage::GAME_SELECTED:
if (value && strlen(value)) {
gamePath_ = Path(value);
} else {
gamePath_.clear();
}
break;
default:
break;
}
}
@ -481,19 +477,6 @@ void UIScreenWithGameBackground::sendMessage(UIMessage message, const char *valu
}
}
void UIDialogScreenWithGameBackground::DrawBackground(UIContext &dc) {
using namespace UI;
using namespace Draw;
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
if (!gamePath_.empty()) {
DrawGameBackground(dc, gamePath_, x, y, z, (g_Config.bTransparentBackground || forceTransparent_), darkenGameBackground_);
} else {
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
}
}
void UIDialogScreenWithGameBackground::sendMessage(UIMessage message, const char *value) {
if (message == UIMessage::SHOW_SETTINGS && screenManager()->topScreen() == this) {
screenManager()->push(new GameSettingsScreen(gamePath_));
@ -506,13 +489,6 @@ void UIScreenWithBackground::sendMessage(UIMessage message, const char *value) {
HandleCommonMessages(message, value, screenManager(), this);
}
void UIDialogScreenWithBackground::DrawBackground(UIContext &dc) {
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
}
void UIDialogScreenWithBackground::AddStandardBack(UI::ViewGroup *parent) {
using namespace UI;
auto di = GetI18NCategory(I18NCat::DIALOG);
@ -756,10 +732,10 @@ void LogoScreen::touch(const TouchInput &touch) {
}
}
void LogoScreen::render() {
void LogoScreen::render(ScreenRenderMode mode) {
using namespace Draw;
UIScreen::render();
UIScreen::render(mode);
UIContext &dc = *screenManager()->getUIContext();
const Bounds &bounds = dc.GetBounds();
@ -895,8 +871,8 @@ void CreditsScreen::update() {
UpdateUIState(UISTATE_MENU);
}
void CreditsScreen::render() {
UIScreen::render();
void CreditsScreen::render(ScreenRenderMode mode) {
UIScreen::render(mode);
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);

View File

@ -36,19 +36,31 @@ void UIBackgroundShutdown();
inline void NoOpVoidBool(bool) {}
class BackgroundScreen : public UIScreen {
public:
void render(ScreenRenderMode mode) override;
void sendMessage(UIMessage message, const char *value) override;
private:
void CreateViews() override {}
const char *tag() const override { return "bg"; }
Path gamePath_;
};
// This doesn't have anything to do with the background anymore. It's just a PPSSPP UIScreen
// that knows how handle sendMessage properly. Same for all the below.
class UIScreenWithBackground : public UIScreen {
public:
UIScreenWithBackground() : UIScreen() {}
protected:
void DrawBackground(UIContext &dc) override;
void sendMessage(UIMessage message, const char *value) override;
};
class UIScreenWithGameBackground : public UIScreenWithBackground {
public:
UIScreenWithGameBackground(const std::string &gamePath)
: UIScreenWithBackground(), gamePath_(gamePath) {}
void DrawBackground(UIContext &dc) override;
UIScreenWithGameBackground(const Path &gamePath) : UIScreenWithBackground(), gamePath_(gamePath) {}
void sendMessage(UIMessage message, const char *value) override;
protected:
Path gamePath_;
@ -61,9 +73,7 @@ class UIDialogScreenWithBackground : public UIDialogScreen {
public:
UIDialogScreenWithBackground() : UIDialogScreen() {}
protected:
void DrawBackground(UIContext &dc) override;
void sendMessage(UIMessage message, const char *value) override;
void AddStandardBack(UI::ViewGroup *parent);
};
@ -71,13 +81,9 @@ class UIDialogScreenWithGameBackground : public UIDialogScreenWithBackground {
public:
UIDialogScreenWithGameBackground(const Path &gamePath)
: UIDialogScreenWithBackground(), gamePath_(gamePath) {}
void DrawBackground(UIContext &dc) override;
void sendMessage(UIMessage message, const char *value) override;
protected:
Path gamePath_;
bool forceTransparent_ = false;
bool darkenGameBackground_ = true;
};
class PromptScreen : public UIDialogScreenWithGameBackground {
@ -140,7 +146,7 @@ public:
bool key(const KeyInput &key) override;
void touch(const TouchInput &touch) override;
void update() override;
void render() override;
void render(ScreenRenderMode mode) override;
void sendMessage(UIMessage message, const char *value) override;
void CreateViews() override {}
@ -158,7 +164,7 @@ class CreditsScreen : public UIDialogScreenWithBackground {
public:
CreditsScreen();
void update() override;
void render() override;
void render(ScreenRenderMode mode) override;
void CreateViews() override;

View File

@ -798,7 +798,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
g_screenManager->switchScreen(new LogoScreen(AfterLogoScreen::DEFAULT));
}
g_screenManager->SetOverlayScreen(new OSDOverlayScreen());
g_screenManager->SetBackgroundOverlayScreens(new BackgroundScreen(), new OSDOverlayScreen());
// Easy testing
// screenManager->push(new GPUDriverTestScreen());
@ -956,6 +956,7 @@ void NativeShutdownGraphics() {
if (g_screenManager) {
g_screenManager->deviceLost();
}
g_iconCache.ClearTextures();
if (gpu)
gpu->DeviceLost();
@ -1104,7 +1105,6 @@ void NativeFrame(GraphicsContext *graphicsContext) {
Achievements::Idle();
g_DownloadManager.Update();
g_screenManager->update();
g_Discord.Update();
g_BackgroundAudio.Play();
@ -1124,6 +1124,10 @@ void NativeFrame(GraphicsContext *graphicsContext) {
g_BackgroundAudio.Update();
}
g_iconCache.FrameUpdate();
g_screenManager->update();
// Apply the UIContext bounds as a 2D transformation matrix.
// TODO: This should be moved into the draw context...
Matrix4x4 ortho = ComputeOrthoMatrix(g_display.dp_xres, g_display.dp_yres);

View File

@ -517,8 +517,8 @@ void OSDOverlayScreen::CreateViews() {
osmView_ = root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f)));
}
void OSDOverlayScreen::render() {
UIScreen::render();
void OSDOverlayScreen::render(ScreenRenderMode mode) {
UIScreen::render(mode);
DebugOverlay debugOverlay = (DebugOverlay)g_Config.iDebugOverlay;

View File

@ -41,7 +41,7 @@ public:
bool UnsyncTouch(const TouchInput &touch) override;
void CreateViews() override;
void render() override;
void render(ScreenRenderMode mode) override;
void update() override;
private:

View File

@ -166,26 +166,29 @@ ReportScreen::ReportScreen(const Path &gamePath)
ratingEnabled_ = enableReporting_;
}
void ReportScreen::postRender() {
// We do this after render because we need it to be within the frame (so the screenshot works).
// We could do it mid frame, but then we have to reapply viewport/scissor.
if (!tookScreenshot_) {
Path path = GetSysDirectory(DIRECTORY_SCREENSHOT);
if (!File::Exists(path)) {
File::CreateDir(path);
}
screenshotFilename_ = path / ".reporting.jpg";
if (TakeGameScreenshot(screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) {
// Redo the views already, now with a screenshot included.
RecreateViews();
} else {
// Good news (?), the views are good as-is without a screenshot.
screenshotFilename_.clear();
}
tookScreenshot_ = true;
}
void ReportScreen::render(ScreenRenderMode mode) {
UIScreen::render(mode);
UIDialogScreenWithGameBackground::postRender();
if (mode & ScreenRenderMode::TOP) {
// We do this after render because we need it to be within the frame (so the screenshot works).
// We could do it mid frame, but then we have to reapply viewport/scissor.
if (!tookScreenshot_) {
Path path = GetSysDirectory(DIRECTORY_SCREENSHOT);
if (!File::Exists(path)) {
File::CreateDir(path);
}
screenshotFilename_ = path / ".reporting.jpg";
if (TakeGameScreenshot(screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) {
// Redo the views already, now with a screenshot included.
RecreateViews();
} else {
// Good news (?), the views are good as-is without a screenshot.
screenshotFilename_.clear();
}
tookScreenshot_ = true;
}
}
}
void ReportScreen::update() {

View File

@ -40,7 +40,7 @@ public:
const char *tag() const override { return "Report"; }
protected:
void postRender() override;
void render(ScreenRenderMode mode) override;
void update() override;
void resized() override;
void CreateViews() override;