From d8b8a51309b2d79fb94351d7411d5f35242cb78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 8 Feb 2026 17:07:22 +0100 Subject: [PATCH] Correct a bunch of edge cases, move some code around --- Common/UI/Screen.cpp | 9 +- Common/UI/Screen.h | 2 +- GPU/Common/FramebufferManagerCommon.cpp | 8 +- UI/EmuScreen.cpp | 114 +++++++++--------------- UI/EmuScreen.h | 4 +- UI/MiscViews.cpp | 30 +++++++ UI/MiscViews.h | 13 +++ 7 files changed, 102 insertions(+), 78 deletions(-) diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 51ee852774..4dc7e0aa90 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -215,8 +215,13 @@ ScreenRenderFlags ScreenManager::render() { // First, go through the whole stack and have every screen render any non-backbuffer render passes. // In EmuScreen, this might result in running emulation. - for (auto &layer : stack_) { - flags |= layer.screen->PreRender(); + for (size_t i = 0; i < stack_.size(); i++) { + const auto &layer = stack_[i]; + ScreenRenderMode mode = ScreenRenderMode::DEFAULT; + if (i == stack_.size() - 1) { + mode |= ScreenRenderMode::TOP; + } + flags |= layer.screen->PreRender(mode); } // Now, start the final render pass. This is now the ONLY place where binding the null fb is allowed. diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 99c8229aee..9f9b7fd886 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -77,7 +77,7 @@ public: virtual void onFinish(DialogResult reason) {} virtual void update() {} - virtual ScreenRenderFlags PreRender() { return ScreenRenderFlags::NONE; } + virtual ScreenRenderFlags PreRender(ScreenRenderMode mode) { return ScreenRenderFlags::NONE; } virtual ScreenRenderFlags render(ScreenRenderMode mode) = 0; virtual void resized() {} virtual void dialogFinished(const Screen *dialog, DialogResult result) {} diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 0102376cd8..93e3055bb3 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1559,9 +1559,11 @@ bool FramebufferManagerCommon::DrawFramebufferToOutput(const DisplayLayoutConfig constexpr float u0 = 0.0f, u1 = 480.0f / 512.0f; constexpr float v0 = 0.0f, v1 = 1.0f; - presentation_->UpdateUniforms(textureCache_->VideoIsPlaying()); - presentation_->SourceTexture(pixelsTex, 512, 272); - presentation_->RunPostshaderPasses(config, flags, uvRotation, u0, v0, u1, v1); + if (useBufferedRendering_) { + presentation_->UpdateUniforms(textureCache_->VideoIsPlaying()); + presentation_->SourceTexture(pixelsTex, 512, 272); + presentation_->RunPostshaderPasses(config, flags, uvRotation, u0, v0, u1, v1); + } // PresentationCommon sets all kinds of state, we can't rely on anything. gstate_c.Dirty(DIRTY_ALL); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 09f9e54167..25cb64cbcb 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -93,6 +93,7 @@ using namespace std::placeholders; #include "UI/ControlMappingScreen.h" #include "UI/DisplayLayoutScreen.h" #include "UI/GameSettingsScreen.h" +#include "UI/MiscViews.h" #include "UI/ProfilerDraw.h" #include "UI/DiscordIntegration.h" #include "UI/ChatScreen.h" @@ -1231,52 +1232,6 @@ void EmuScreen::touch(const TouchInput &touch) { } } -class GameInfoBGView : public UI::InertView { -public: - GameInfoBGView(const Path &gamePath, UI::LayoutParams *layoutParams) : InertView(layoutParams), gamePath_(gamePath) {} - - void Draw(UIContext &dc) override { - // Should only be called when visible. - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GameInfoFlags::PIC1); - dc.Flush(); - - // PIC1 is the loading image, so let's only draw if it's available. - if (ginfo->Ready(GameInfoFlags::PIC1) && ginfo->pic1.texture) { - Draw::Texture *texture = ginfo->pic1.texture; - if (texture) { - const DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()); - // Similar to presentation, we want to put the game PIC1 in the same region of the screen. - FRect frame = GetScreenFrame(config.bIgnoreScreenInsets, g_display.pixel_xres, g_display.pixel_yres); - FRect rc; - CalculateDisplayOutputRect(config, &rc, texture->Width(), texture->Height(), frame, config.iInternalScreenRotation); - - // Need to adjust for DPI here since we're still in the UI coordinate space here, not the pixel coordinate space used for in-game presentation. - Bounds bounds(rc.x * g_display.dpi_scale_x, rc.y * g_display.dpi_scale_y, rc.w * g_display.dpi_scale_x, rc.h * g_display.dpi_scale_y); - - dc.GetDrawContext()->BindTexture(0, texture); - - double loadTime = ginfo->pic1.timeLoaded; - uint32_t color = alphaMul(color_, ease((time_now_d() - loadTime) * 3)); - dc.Draw()->DrawTexRect(bounds, 0, 0, 1, 1, color); - dc.Flush(); - dc.RebindTexture(); - } - } - } - - std::string DescribeText() const override { - return ""; - } - - void SetColor(uint32_t c) { - color_ = c; - } - -protected: - Path gamePath_; - uint32_t color_ = 0xFFC0C0C0; -}; - // TODO: Shouldn't actually need bounds for this, Anchor can center too. static UI::AnchorLayoutParams *AnchorInCorner(const Bounds &bounds, int corner, float xOffset, float yOffset) { using namespace UI; @@ -1627,15 +1582,29 @@ void EmuScreen::HandleFlip() { #endif } -ScreenRenderFlags EmuScreen::PreRender() { +bool EmuScreen::ShouldRunEmulation(ScreenRenderMode mode) const { + if (!(mode & ScreenRenderMode::TOP) && !ShouldRunBehind() && strcmp(screenManager()->topScreen()->tag(), "DevMenu") != 0) { + return false; + } + return true; +} + +ScreenRenderFlags EmuScreen::PreRender(ScreenRenderMode mode) { // If a boot is in progress, update it. ProcessGameBoot(gamePath_); using namespace Draw; skipBufferEffects_ = g_Config.bSkipBufferEffects; if (!skipBufferEffects_) { - // We need to run emulation here, and perform all the normal render passes. - return RunEmulation(false); + if (ShouldRunEmulation(mode)) { + // We need to run emulation here, and perform all the normal render passes. + return RunEmulation(false); + } else { + const DeviceOrientation orientation = GetDeviceOrientation(); + const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation); + // We run just the post shaders. + gpu->PrepareCopyDisplayToOutput(displayLayoutConfig); + } } return ScreenRenderFlags::NONE; } @@ -1653,6 +1622,31 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { ScreenRenderFlags screenRenderFlags = ScreenRenderFlags::NONE; + if (mode & ScreenRenderMode::TOP) { + System_Notify(SystemNotification::KEEP_SCREEN_AWAKE); + } + + const DeviceOrientation orientation = GetDeviceOrientation(); + const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation); + + if (!skipBufferEffects_ && !ShouldRunEmulation(mode)) { + if (gpu) { + gpu->CopyDisplayToOutput(displayLayoutConfig); + } + darken(); + return screenRenderFlags; + } + + if (!PSP_IsInited() || readyToFinishBoot_) { + // It's possible this might be set outside PSP_RunLoopFor(). + // In this case, we need to double check it here. + if (mode & ScreenRenderMode::TOP) { + checkPowerDown(); + } + renderUI(); + return screenRenderFlags; + } + if (skipBufferEffects_) { // In skip buffer effects mode, we run emulation *after* the backbuffer bind. screenRenderFlags = RunEmulation(true); @@ -1666,33 +1660,11 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { const bool skipBufferEffects = skipBufferEffects_; - const DeviceOrientation orientation = GetDeviceOrientation(); - const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation); - // Gotta copy the output at some point. Also this is where we take the screenshot if needed. if (gpu) { gpu->CopyDisplayToOutput(displayLayoutConfig); } - if (mode & ScreenRenderMode::TOP) { - System_Notify(SystemNotification::KEEP_SCREEN_AWAKE); - } else if (!ShouldRunBehind() && strcmp(screenManager()->topScreen()->tag(), "DevMenu") != 0) { - // Need to make sure the UI texture is available, for "darken". - darken(); - return screenRenderFlags; - } - - if (!PSP_IsInited() || readyToFinishBoot_) { - // It's possible this might be set outside PSP_RunLoopFor(). - // In this case, we need to double check it here. - if (mode & ScreenRenderMode::TOP) { - checkPowerDown(); - } - // Need to make sure the UI texture is available, for "darken". - renderUI(); - return screenRenderFlags; - } - runImDebugger(); Draw::BackendState state = draw->GetCurrentBackendState(); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 2e87c5a452..a902d2ef39 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -70,7 +70,7 @@ public: protected: void darken(); void focusChanged(ScreenFocusChange focusChange) override; - ScreenRenderFlags PreRender() override; + ScreenRenderFlags PreRender(ScreenRenderMode mode) override; private: void CreateViews() override; @@ -96,6 +96,8 @@ private: void ProcessQueuedVKeys(); void ProcessVKey(VirtKey vkey); + bool ShouldRunEmulation(ScreenRenderMode mode) const; + UI::Event OnDevMenu; UI::Event OnChatMenu; bool bootPending_ = true; diff --git a/UI/MiscViews.cpp b/UI/MiscViews.cpp index b26a620faa..b36068d173 100644 --- a/UI/MiscViews.cpp +++ b/UI/MiscViews.cpp @@ -11,6 +11,7 @@ #include "UI/GameInfoCache.h" #include "Common/UI/PopupScreens.h" #include "Core/Config.h" +#include "GPU/Common/PresentationCommon.h" TextWithImage::TextWithImage(ImageID imageID, std::string_view text, UI::LinearLayoutParams *layoutParams) : UI::LinearLayout(ORIENT_HORIZONTAL, layoutParams) { using namespace UI; @@ -259,3 +260,32 @@ void AddRotationPicker(ScreenManager *screenManager, UI::ViewGroup *parent, bool System_Notify(SystemNotification::ROTATE_UPDATED); }); } + +void GameInfoBGView::Draw(UIContext &dc) { + // Should only be called when visible. + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GameInfoFlags::PIC1); + dc.Flush(); + + // PIC1 is the loading image, so let's only draw if it's available. + if (ginfo->Ready(GameInfoFlags::PIC1) && ginfo->pic1.texture) { + Draw::Texture *texture = ginfo->pic1.texture; + if (texture) { + const DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()); + // Similar to presentation, we want to put the game PIC1 in the same region of the screen. + FRect frame = GetScreenFrame(config.bIgnoreScreenInsets, g_display.pixel_xres, g_display.pixel_yres); + FRect rc; + CalculateDisplayOutputRect(config, &rc, texture->Width(), texture->Height(), frame, config.iInternalScreenRotation); + + // Need to adjust for DPI here since we're still in the UI coordinate space here, not the pixel coordinate space used for in-game presentation. + Bounds bounds(rc.x * g_display.dpi_scale_x, rc.y * g_display.dpi_scale_y, rc.w * g_display.dpi_scale_x, rc.h * g_display.dpi_scale_y); + + dc.GetDrawContext()->BindTexture(0, texture); + + double loadTime = ginfo->pic1.timeLoaded; + uint32_t color = alphaMul(color_, ease((time_now_d() - loadTime) * 3)); + dc.Draw()->DrawTexRect(bounds, 0, 0, 1, 1, color); + dc.Flush(); + dc.RebindTexture(); + } + } +} diff --git a/UI/MiscViews.h b/UI/MiscViews.h index cc852e8516..79b31df944 100644 --- a/UI/MiscViews.h +++ b/UI/MiscViews.h @@ -78,4 +78,17 @@ private: float scale_ = 1.0f; }; +class GameInfoBGView : public UI::InertView { +public: + GameInfoBGView(const Path &gamePath, UI::LayoutParams *layoutParams) : InertView(layoutParams), gamePath_(gamePath) {} + + void Draw(UIContext &dc) override; + std::string DescribeText() const override { return ""; } + void SetColor(uint32_t c) { color_ = c; } + +protected: + Path gamePath_; + uint32_t color_ = 0xFFC0C0C0; +}; + void AddRotationPicker(ScreenManager *screenManager, UI::ViewGroup *parent, bool text);