diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.cpp b/Common/GPU/Vulkan/VulkanFramebuffer.cpp index 3babe92fa0c0..3f77f24efe6c 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.cpp +++ b/Common/GPU/Vulkan/VulkanFramebuffer.cpp @@ -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) { diff --git a/Common/System/System.h b/Common/System/System.h index 1568e43ed5b1..643e6218213f 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -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, diff --git a/Common/UI/Context.cpp b/Common/UI/Context.cpp index 24b12d203dc8..da46f9d004fb 100644 --- a/Common/UI/Context.cpp +++ b/Common/UI/Context.cpp @@ -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; } diff --git a/Common/UI/Context.h b/Common/UI/Context.h index 328596d7c341..4b7cf7470596 100644 --- a/Common/UI/Context.h +++ b/Common/UI/Context.h @@ -113,10 +113,6 @@ class UIContext { 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 @@ class UIContext { std::string lastUIAtlas_; std::string UIAtlas_ = "ui_atlas.zim"; - - const char *screenTag_ = nullptr; }; diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 03337d081c98..b1fe507cfe4a 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -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); - - // 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; + // Collect the screens to render + TinySet layers; + + // 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); } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index ae911b1203f5..ed5b2a957aa7 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -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 @@ class Screen { 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 @@ class Screen { 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 @@ class ScreenManager { 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 @@ class ScreenManager { const Screen *dialogFinished_ = nullptr; DialogResult dialogResult_{}; + Screen *backgroundScreen_ = nullptr; Screen *overlayScreen_ = nullptr; struct Layer { diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index c49dee977c30..99bb251c31df 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -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->SetScreenTag(tag()); + UIContext &uiContext = *screenManager()->getUIContext(); - 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(); } } diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 767cd462cdc2..a5fb9604ad70 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -36,9 +36,7 @@ class UIScreen : public Screen { ~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 @@ class UIScreen : public Screen { protected: virtual void CreateViews() = 0; - virtual void DrawBackground(UIContext &dc) {} void RecreateViews() override { recreateViews_ = true; } bool UseVerticalLayout() const; @@ -74,6 +71,8 @@ class UIScreen : public Screen { bool ignoreInput_ = false; protected: + virtual void DrawBackground(UIContext &ui) {} + void SetupViewport(); void DoRecreateViews(); bool recreateViews_ = true; @@ -111,6 +110,9 @@ class PopupScreen : public UIDialogScreen { 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; } diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 48a11adb68b3..4df546bb4ca8 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -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(); diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index cffdac5f6bfa..69c8a4d449d2 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -147,7 +147,7 @@ class TouchTestScreen : public UIDialogScreenWithGameBackground { } 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; diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 5d845ebb3d49..d82d3a9da87c 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -117,19 +117,15 @@ class DisplayLayoutBackground : public UI::View { 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); } } diff --git a/UI/DisplayLayoutScreen.h b/UI/DisplayLayoutScreen.h index 0af2648bc663..f4a80b0c8330 100644 --- a/UI/DisplayLayoutScreen.h +++ b/UI/DisplayLayoutScreen.h @@ -32,18 +32,19 @@ class DisplayLayoutScreen : public UIDialogScreenWithGameBackground { 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; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 28b922107de8..ec7542c22d93 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -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. - - 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 EmuScreen::canBeBackground() const { + if (g_Config.bSkipBufferEffects) + return false; - 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); + bool forceTransparent = false; // this needs to be true somehow on the display layout screen. + + if (!g_Config.bTransparentBackground && !forceTransparent) + return false; + + return true; } -void EmuScreen::postRender() { - Draw::DrawContext *draw = screenManager()->getDrawContext(); - if (!draw) - return; - if (stopRender_) - draw->WipeQueue(); +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(); + } } -void EmuScreen::render() { +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); + } + 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() { diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 9c9e922afe77..a050fd92660c 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -42,12 +42,11 @@ class EmuScreen : public UIScreen { 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 @@ class EmuScreen : public UIScreen { bool key(const KeyInput &key) override; protected: - + void darken(); void focusChanged(ScreenFocusChange focusChange) override; private: diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 6d725dc41ca4..c10faf17ba85 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -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: diff --git a/UI/GPUDriverTestScreen.h b/UI/GPUDriverTestScreen.h index 38ec2c33e185..16c1f8d7c63c 100644 --- a/UI/GPUDriverTestScreen.h +++ b/UI/GPUDriverTestScreen.h @@ -15,7 +15,7 @@ class GPUDriverTestScreen : public UIDialogScreenWithBackground { ~GPUDriverTestScreen(); void CreateViews() override; - void render() override; + void render(ScreenRenderMode mode) override; const char *tag() const override { return "GPUDriverTest"; } diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 238c8de13acb..aae67e8be313 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -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 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); diff --git a/UI/GameScreen.h b/UI/GameScreen.h index 52c8907ea671..4340fae811bd 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -38,7 +38,7 @@ class GameScreen : public UIDialogScreenWithGameBackground { void update() override; - void render() override; + void render(ScreenRenderMode mode) override; const char *tag() const override { return "Game"; } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 1037f4039b01..881d6ee8bc18 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -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; }); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index f52d00698080..43dab5c02559 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -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; } diff --git a/UI/MemStickScreen.h b/UI/MemStickScreen.h index 62ad37c08265..f409e958153a 100644 --- a/UI/MemStickScreen.h +++ b/UI/MemStickScreen.h @@ -52,11 +52,11 @@ class MemStickScreen : public UIDialogScreenWithBackground { 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. } diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 438bc5f9c57b..76218bf27a07 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -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 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) { - float x, y, z; - screenManager()->getFocusPosition(x, y, z); - ::DrawBackground(dc, 1.0f, x, y, z); - dc.Flush(); -} +void BackgroundScreen::render(ScreenRenderMode mode) { + if (mode & ScreenRenderMode::FIRST) { + SetupViewport(); + } else { + _dbg_assert_(false); + } + + UIContext *uiContext = screenManager()->getUIContext(); + + uiContext->PushTransform({ translation_, scale_, alpha_ }); -void UIScreenWithGameBackground::DrawBackground(UIContext &dc) { + uiContext->Begin(); float x, y, z; screenManager()->getFocusPosition(x, y, z); + if (!gamePath_.empty()) { - DrawGameBackground(dc, gamePath_, x, y, z, (g_Config.bTransparentBackground || forceTransparent_), darkenGameBackground_); + ::DrawGameBackground(*uiContext, gamePath_, x, y, z); } else { - ::DrawBackground(dc, 1.0f, x, y, z); - dc.Flush(); + ::DrawBackground(*uiContext, 1.0f, x, y, z); + } + + uiContext->Flush(); + + uiContext->PopTransform(); +} + +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); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index df9d991ab797..cc4a6b595f77 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -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 @@ class LogoScreen : public UIScreen { 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; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 1945da4c0200..05e7ec97d82c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -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); diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 5209673883e6..0edaa36b0203 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -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; diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index c22d5478e20a..1440fdca635b 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -41,7 +41,7 @@ class OSDOverlayScreen : public UIScreen { bool UnsyncTouch(const TouchInput &touch) override; void CreateViews() override; - void render() override; + void render(ScreenRenderMode mode) override; void update() override; private: diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 92136d456262..5ec3415785ef 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -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(); +void ReportScreen::render(ScreenRenderMode mode) { + UIScreen::render(mode); + + 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; } - tookScreenshot_ = true; } - - UIDialogScreenWithGameBackground::postRender(); } void ReportScreen::update() { diff --git a/UI/ReportScreen.h b/UI/ReportScreen.h index 006d90e6f5d5..cd7c55ab10d6 100644 --- a/UI/ReportScreen.h +++ b/UI/ReportScreen.h @@ -40,7 +40,7 @@ class ReportScreen : public UIDialogScreenWithGameBackground { 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;