diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.cpp b/Common/GPU/Vulkan/VulkanFramebuffer.cpp index 7bc0bff93e..f36b1eb4c6 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.cpp +++ b/Common/GPU/Vulkan/VulkanFramebuffer.cpp @@ -292,6 +292,11 @@ static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) { VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType, VkSampleCountFlagBits sampleCount) { bool isBackbuffer = rpType == RenderPassType::BACKBUFFER; + if (isBackbuffer) { + _dbg_assert_(key.colorLoadAction != VKRRenderPassLoadAction::KEEP); + _dbg_assert_(key.depthLoadAction != VKRRenderPassLoadAction::KEEP); + _dbg_assert_(key.stencilLoadAction != VKRRenderPassLoadAction::KEEP); + } bool hasDepth = RenderPassTypeHasDepth(rpType); bool multiview = RenderPassTypeHasMultiView(rpType); bool multisample = RenderPassTypeHasMultisample(rpType); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index a376b8dcd8..4c26d78187 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -982,20 +982,27 @@ void VulkanRenderManager::EndCurRenderStep() { curPipelineFlags_ = (PipelineFlags)0; } -void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) { +void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction colorLoad, VKRRenderPassLoadAction depthLoad, VKRRenderPassLoadAction stencilLoad, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) { _dbg_assert_(insideFrame_); + if (!fb) { + // Backbuffer render passes have some requirements. + _dbg_assert_(colorLoad != VKRRenderPassLoadAction::KEEP); + _dbg_assert_(depthLoad != VKRRenderPassLoadAction::KEEP); + _dbg_assert_(stencilLoad != VKRRenderPassLoadAction::KEEP); + } + // Eliminate dupes (bind of the framebuffer we already are rendering to), instantly convert to a clear if possible. if (!steps_.empty() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) { u32 clearMask = 0; - if (color == VKRRenderPassLoadAction::CLEAR) { + if (colorLoad == VKRRenderPassLoadAction::CLEAR) { clearMask |= VK_IMAGE_ASPECT_COLOR_BIT; } - if (depth == VKRRenderPassLoadAction::CLEAR) { + if (depthLoad == VKRRenderPassLoadAction::CLEAR) { clearMask |= VK_IMAGE_ASPECT_DEPTH_BIT; curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL; } - if (stencil == VKRRenderPassLoadAction::CLEAR) { + if (stencilLoad == VKRRenderPassLoadAction::CLEAR) { clearMask |= VK_IMAGE_ASPECT_STENCIL_BIT; curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL; } @@ -1057,25 +1064,25 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR // Older Mali drivers have issues with depth and stencil don't match load/clear/etc. // TODO: Determine which versions and do this only where necessary. u32 lateClearMask = 0; - if (depth != stencil && vulkan_->GetPhysicalDeviceProperties().properties.vendorID == VULKAN_VENDOR_ARM) { - if (stencil == VKRRenderPassLoadAction::DONT_CARE) { - stencil = depth; - } else if (depth == VKRRenderPassLoadAction::DONT_CARE) { - depth = stencil; - } else if (stencil == VKRRenderPassLoadAction::CLEAR) { - depth = stencil; + if (depthLoad != stencilLoad && vulkan_->GetPhysicalDeviceProperties().properties.vendorID == VULKAN_VENDOR_ARM) { + if (stencilLoad == VKRRenderPassLoadAction::DONT_CARE) { + stencilLoad = depthLoad; + } else if (depthLoad == VKRRenderPassLoadAction::DONT_CARE) { + depthLoad = stencilLoad; + } else if (stencilLoad == VKRRenderPassLoadAction::CLEAR) { + depthLoad = stencilLoad; lateClearMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } else if (depth == VKRRenderPassLoadAction::CLEAR) { - stencil = depth; + } else if (depthLoad == VKRRenderPassLoadAction::CLEAR) { + stencilLoad = depthLoad; lateClearMask |= VK_IMAGE_ASPECT_DEPTH_BIT; } } VKRStep *step = new VKRStep{ VKRStepType::RENDER }; step->render.framebuffer = fb; - step->render.colorLoad = color; - step->render.depthLoad = depth; - step->render.stencilLoad = stencil; + step->render.colorLoad = colorLoad; + step->render.depthLoad = depthLoad; + step->render.stencilLoad = stencilLoad; step->render.colorStore = VKRRenderPassStoreAction::STORE; step->render.depthStore = VKRRenderPassStoreAction::STORE; step->render.stencilStore = VKRRenderPassStoreAction::STORE; @@ -1092,7 +1099,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR if (fb) { // If there's a KEEP, we naturally read from the framebuffer. - if (color == VKRRenderPassLoadAction::KEEP || depth == VKRRenderPassLoadAction::KEEP || stencil == VKRRenderPassLoadAction::KEEP) { + if (colorLoad == VKRRenderPassLoadAction::KEEP || depthLoad == VKRRenderPassLoadAction::KEEP || stencilLoad == VKRRenderPassLoadAction::KEEP) { step->dependencies.insert(fb); } } @@ -1118,7 +1125,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR } } - if (color == VKRRenderPassLoadAction::CLEAR || depth == VKRRenderPassLoadAction::CLEAR || stencil == VKRRenderPassLoadAction::CLEAR) { + if (colorLoad == VKRRenderPassLoadAction::CLEAR || depthLoad == VKRRenderPassLoadAction::CLEAR || stencilLoad == VKRRenderPassLoadAction::CLEAR) { curRenderArea_.SetRect(0, 0, curWidth_, curHeight_); } diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index c7e93fbf82..d558bc6b95 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1839,6 +1839,8 @@ DataFormat VKContext::PreferredFramebufferReadbackFormat(Framebuffer *src) { } void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) { + _dbg_assert_(fbo != nullptr || equals(tag, "BackBuffer")) + VKFramebuffer *fb = (VKFramebuffer *)fbo; VKRRenderPassLoadAction color = (VKRRenderPassLoadAction)rp.color; VKRRenderPassLoadAction depth = (VKRRenderPassLoadAction)rp.depth; diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 2c4407009f..51ee852774 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -209,7 +209,25 @@ void ScreenManager::resized() { } ScreenRenderFlags ScreenManager::render() { + using namespace Draw; + ScreenRenderFlags flags = ScreenRenderFlags::NONE; + + // 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(); + } + + // Now, start the final render pass. This is now the ONLY place where binding the null fb is allowed. + draw_->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR}, "BackBuffer"); + getUIContext()->BeginFrame(); + + const Draw::Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f}; + draw_->SetViewport(viewport); + draw_->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres); + draw_->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres); + if (!stack_.empty()) { // Collect the screens to render TinySet layers; diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index b7486e6e3f..99c8229aee 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -72,11 +72,12 @@ ENUM_CLASS_BITOPS(ScreenRenderRole); class Screen { public: - Screen() : screenManager_(nullptr) { } + Screen() = default; virtual ~Screen(); virtual void onFinish(DialogResult reason) {} virtual void update() {} + virtual ScreenRenderFlags PreRender() { return ScreenRenderFlags::NONE; } virtual ScreenRenderFlags render(ScreenRenderMode mode) = 0; virtual void resized() {} virtual void dialogFinished(const Screen *dialog, DialogResult result) {} @@ -114,7 +115,7 @@ protected: } private: - ScreenManager *screenManager_; + ScreenManager *screenManager_ = nullptr; int token_ = -1; DISALLOW_COPY_AND_ASSIGN(Screen); diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index a9d7c983b9..163874a825 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -200,30 +200,7 @@ void UIScreen::deviceRestored(Draw::DrawContext *draw) { root_->DeviceRestored(draw); } -void UIScreen::SetupViewport() { - using namespace Draw; - Draw::DrawContext *draw = screenManager()->getDrawContext(); - _dbg_assert_(draw != nullptr); - // Bind and clear the back buffer - draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI"); - screenManager()->getUIContext()->BeginFrame(); - - Draw::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); -} - ScreenRenderFlags UIScreen::render(ScreenRenderMode mode) { - if (mode & ScreenRenderMode::FIRST) { - SetupViewport(); - } - DoRecreateViews(); UIContext &uiContext = *screenManager()->getUIContext(); diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index c6b55cc679..3127464d42 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -77,7 +77,6 @@ protected: virtual void DrawBackground(UIContext &ui) {} virtual void DrawForeground(UIContext &ui) {} - void SetupViewport(); void DoRecreateViews(); bool recreateViews_ = true; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 56669862eb..b0891ea50f 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -662,7 +662,6 @@ void __DisplayFlip(int cyclesLate) { } if (nextFrame) { gpu->SetCurFramebufferDirty(fbReallyDirty); - gpu->CopyDisplayToOutput(g_displayLayoutConfigCached); if (fbReallyDirty) { DisplayFireActualFlip(); } diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 8a0055e5f1..166b0fbe50 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1665,6 +1665,8 @@ void FramebufferManagerCommon::PrepareCopyDisplayToOutput(const DisplayLayoutCon } } else { DEBUG_LOG(Log::FrameBuf, "Found no FBO to display! displayFBPtr = %08x", fbaddr); + // No framebuffer to display! Clear to black. + // TODO: Draw a black rectangle, will be important once we add backgrounds. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); // No framebuffer to display! Clear to black. presentation_->SourceBlank(); @@ -3434,10 +3436,6 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag) { if (!dst->fbo || !src->fbo || !useBufferedRendering_) { // This can happen if they recently switched from non-buffered. - if (useBufferedRendering_) { - // Just bind the back buffer for rendering, forget about doing anything else as we're in a weird state. - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer"); - } return; } diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 1efca22c4a..045d8dbd23 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -898,7 +898,6 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config) { int lastWidth = srcWidth_; int lastHeight = srcHeight_; - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "FinalBlit"); draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); if (!srcFramebuffer_ && !srcTexture_) { diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 49d185caa6..b4349e3437 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -125,6 +125,7 @@ public: virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) = 0; virtual void SetCurFramebufferDirty(bool dirty) = 0; + virtual void PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) = 0; virtual void CopyDisplayToOutput(const DisplayLayoutConfig &config) = 0; virtual bool PresentedThisFrame() const = 0; diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 7e87e6a9c1..042fd3a16a 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -530,7 +530,7 @@ void GPUCommonHW::PreExecuteOp(u32 op, u32 diff) { CheckFlushOp(op >> 24, diff); } -void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config) { +void GPUCommonHW::PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) { drawEngineCommon_->FlushQueuedDepth(); // Flush anything left over. drawEngineCommon_->Flush(); @@ -539,6 +539,9 @@ void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config) { // after this, render pass is active. framebufferManager_->PrepareCopyDisplayToOutput(config, curFramebufferDirty_); +} + +void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config) { framebufferManager_->CopyDisplayToOutput(config); curFramebufferDirty_ = false; } diff --git a/GPU/GPUCommonHW.h b/GPU/GPUCommonHW.h index f2179e6b9a..6b1032c91e 100644 --- a/GPU/GPUCommonHW.h +++ b/GPU/GPUCommonHW.h @@ -11,6 +11,7 @@ public: // This can fail, and if so no render pass is active. void SetCurFramebufferDirty(bool dirty) override { curFramebufferDirty_ = dirty; } + void PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) override; void CopyDisplayToOutput(const DisplayLayoutConfig &config) override; void DoState(PointerWrap &p) override; void DeviceLost() override; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index f8f9c9b7a2..39b943c972 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -624,7 +624,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(const DisplayLayoutConfig &config, u1 = 1.0f; } if (!hasImage) { - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "CopyToCurrentFboFromDisplayRam"); + presentation_->SourceBlank(); presentation_->NotifyPresent(); return; } @@ -644,13 +644,16 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(const DisplayLayoutConfig &config, presentation_->SourceTexture(fbTex, desc.width, desc.height); presentation_->RunPostshaderPasses(config, outputFlags, config.iInternalScreenRotation, u0, v0, u1, v1); - presentation_->CopyToOutput(config); } -void SoftGPU::CopyDisplayToOutput(const DisplayLayoutConfig &config) { +void SoftGPU::PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) { drawEngine_->transformUnit.Flush(this, "output"); // The display always shows 480x272. CopyToCurrentFboFromDisplayRam(config, FB_WIDTH, FB_HEIGHT); +} + +void SoftGPU::CopyDisplayToOutput(const DisplayLayoutConfig &config) { + presentation_->CopyToOutput(config); MarkDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::CLEAR); } diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 8c276d304c..0cd5182cf9 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -138,6 +138,7 @@ public: void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override; void SetCurFramebufferDirty(bool dirty) override {} + void PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) override; void CopyDisplayToOutput(const DisplayLayoutConfig &config) override; void GetStats(char *buffer, size_t bufsize) override; std::vector GetFramebufferList() const override { return std::vector(); } diff --git a/UI/Background.cpp b/UI/Background.cpp index b4bfac0629..dc8732d537 100644 --- a/UI/Background.cpp +++ b/UI/Background.cpp @@ -446,7 +446,7 @@ void DrawBackground(UIContext &dc, float alpha, Lin::Vec3 focus) { } uint32_t GetBackgroundColorWithAlpha(const UIContext &dc) { - return colorAlpha(colorBlend(dc.GetTheme().backgroundColor, 0, 0.5f), 0.65f); // 0.65 = 166 = A6 + return colorAlpha(colorBlend(dc.GetTheme().backgroundColor, 0, 0.5f), 0.72f); // 0.72 = 183 = B7 } enum class BackgroundFillMode { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 00a225e528..3ed7f7d17a 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1627,14 +1627,23 @@ void EmuScreen::HandleFlip() { #endif } +ScreenRenderFlags EmuScreen::PreRender() { + // 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); + } + return ScreenRenderFlags::NONE; +} + ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { // Moved from update, because we want it to be possible for booting to happen even when the screen // is in the background, like when choosing Reset from the pause menu. - // If a boot is in progress, update it. - ProcessGameBoot(gamePath_); - - const Draw::Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f}; using namespace Draw; DrawContext *draw = screenManager()->getDrawContext(); @@ -1642,81 +1651,35 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { return ScreenRenderFlags::NONE; // shouldn't really happen but I've seen a suspicious stack trace.. } - ProcessQueuedVKeys(); + ScreenRenderFlags screenRenderFlags = ScreenRenderFlags::NONE; - const bool skipBufferEffects = g_Config.bSkipBufferEffects; - - bool framebufferBound = false; - - if (mode & ScreenRenderMode::FIRST) { - // Actually, always gonna be first when it exists (?) - - // 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 (skipBufferEffects && !g_Config.bSoftwareRendering) { - // 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::CLEAR, RPAction::CLEAR}, "EmuScreen_BackBuffer"); - } else { - draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000}, "EmuScreen_BackBuffer"); - } - - draw->SetViewport(viewport); - draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres); - framebufferBound = true; - } - draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres); - } else { - // Some other screen bound the backbuffer first. - framebufferBound = true; + if (skipBufferEffects_) { + // In skip buffer effects mode, we run emulation *after* the backbuffer bind. + screenRenderFlags = RunEmulation(true); } - g_OSD.NudgeIngameNotifications(); + // We might have a bad viewport after RunEmulation, reset. + Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f}; + draw->SetViewport(viewport); + + ProcessQueuedVKeys(); + + const bool skipBufferEffects = skipBufferEffects_; const DeviceOrientation orientation = GetDeviceOrientation(); const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation); - __DisplaySetDisplayLayoutConfig(displayLayoutConfig); + + // Gotta copy the output at some point. + if (gpu) { + gpu->CopyDisplayToOutput(displayLayoutConfig); + } if (mode & ScreenRenderMode::TOP) { System_Notify(SystemNotification::KEEP_SCREEN_AWAKE); } else if (!ShouldRunBehind() && strcmp(screenManager()->topScreen()->tag(), "DevMenu") != 0) { - // NOTE: The strcmp is != 0 - so all popped-over screens EXCEPT DevMenu - // Just to make sure. - if (PSP_IsInited() && !skipBufferEffects) { - _dbg_assert_(gpu); - gpu->BeginHostFrame(displayLayoutConfig); - gpu->SetCurFramebufferDirty(true); - gpu->CopyDisplayToOutput(displayLayoutConfig); - gpu->EndHostFrame(); - } - if (gpu && gpu->PresentedThisFrame()) { - framebufferBound = true; - } - if (!framebufferBound) { - draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR,}, "EmuScreen_Behind"); - } - - Draw::BackendState state = draw->GetCurrentBackendState(); - if (state.valid) { - // The below can trigger when switching from skip-buffer-effects. We don't really care anymore... - // _dbg_assert_msg_(state.passes >= 1, "skipB: %d sw: %d mode: %d back: %d tag: %s behi: %d", (int)skipBufferEffects, (int)g_Config.bSoftwareRendering, (int)mode, (int)g_Config.iGPUBackend, screenManager()->topScreen()->tag(), (int)g_Config.bRunBehindPauseMenu); - // Workaround any remaining bugs like this. - if (state.passes == 0) { - draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR,}, "EmuScreen_SafeFallback"); - } - } - // Need to make sure the UI texture is available, for "darken". - screenManager()->getUIContext()->BeginFrame(); - draw->SetViewport(viewport); - draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres); darken(); - return ScreenRenderFlags::NONE; + return screenRenderFlags; } if (!PSP_IsInited() || readyToFinishBoot_) { @@ -1725,42 +1688,52 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { if (mode & ScreenRenderMode::TOP) { checkPowerDown(); } - draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR}, "EmuScreen_Invalid"); // Need to make sure the UI texture is available, for "darken". - screenManager()->getUIContext()->BeginFrame(); - draw->SetViewport(viewport); - draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres); renderUI(); - return ScreenRenderFlags::NONE; + return screenRenderFlags; } - // Freeze-frame functionality (loads a savestate on every frame). - if (PSP_CoreParameter().freezeNext) { - PSP_CoreParameter().frozen = true; - PSP_CoreParameter().freezeNext = false; - SaveState::SaveToRam(freezeState_); - } else if (PSP_CoreParameter().frozen) { - std::string errorString; - if (CChunkFileReader::ERROR_NONE != SaveState::LoadFromRam(freezeState_, &errorString)) { - ERROR_LOG(Log::SaveState, "Failed to load freeze state (%s). Unfreezing.", errorString.c_str()); - PSP_CoreParameter().frozen = false; - } - } - - // Running it early allows things like direct readbacks of buffers, things we can't do - // when we have started the final render pass. Well, technically we probably could with some manipulation - // of pass order in the render managers.. runImDebugger(); - return RunEmulation(mode, framebufferBound, skipBufferEffects); + Draw::BackendState state = draw->GetCurrentBackendState(); + + if (!(mode & ScreenRenderMode::TOP)) { + renderImDebugger(); + // We're in run-behind mode, but we don't want to draw chat, debug UI and stuff. We do draw the imdebugger though. + // So, darken and bail here. + // Reset viewport/scissor to be sure. + darken(); + return screenRenderFlags; + } + + // NOTE: We don't check for powerdown if we're not the top screen. + checkPowerDown(); + + if (hasVisibleUI()) { + cardboardDisableButton_->SetVisibility(displayLayoutConfig.bEnableCardboardVR ? UI::V_VISIBLE : UI::V_GONE); + renderUI(); + } + + if (chatMenu_ && (chatMenu_->GetVisibility() == UI::V_VISIBLE)) { + SetVRAppMode(VRAppMode::VR_DIALOG_MODE); + } else { + SetVRAppMode(screenManager()->topScreen() == this ? VRAppMode::VR_GAME_MODE : VRAppMode::VR_DIALOG_MODE); + } + + renderImDebugger(); + return screenRenderFlags; } -ScreenRenderFlags EmuScreen::RunEmulation(ScreenRenderMode mode, bool framebufferBound, bool skipBufferEffects) { +ScreenRenderFlags EmuScreen::RunEmulation(bool skipBufferEffects) { using namespace Draw; ScreenRenderFlags flags = ScreenRenderFlags::NONE; + g_OSD.NudgeIngameNotifications(); + const DeviceOrientation orientation = GetDeviceOrientation(); const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation); + __DisplaySetDisplayLayoutConfig(displayLayoutConfig); + DrawContext *draw = screenManager()->getDrawContext(); const Draw::Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f}; @@ -1771,12 +1744,23 @@ ScreenRenderFlags EmuScreen::RunEmulation(ScreenRenderMode mode, bool framebuffe if (gpu) { gpu->BeginHostFrame(displayLayoutConfig); } - if (SaveState::Process()) { - // We might have lost the framebuffer bind if we had one, due to a readback. - if (framebufferBound) { - draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_SavestateRebind"); + + // Freeze-frame functionality (loads a savestate on every frame). + if (PSP_CoreParameter().freezeNext) { + PSP_CoreParameter().frozen = true; + PSP_CoreParameter().freezeNext = false; + SaveState::SaveToRam(freezeState_); + } else if (PSP_CoreParameter().frozen) { + std::string errorString; + if (CChunkFileReader::ERROR_NONE != SaveState::LoadFromRam(freezeState_, &errorString)) { + ERROR_LOG(Log::SaveState, "Failed to load freeze state (%s). Unfreezing.", errorString.c_str()); + PSP_CoreParameter().frozen = false; } } + + if (SaveState::Process()) { + // TODO: investigate. + } PSP_RunLoopWhileState(); // Hopefully, after running, coreState is now CORE_NEXTFRAME @@ -1796,17 +1780,13 @@ ScreenRenderFlags EmuScreen::RunEmulation(ScreenRenderMode mode, bool framebuffe // Clear to blue background screen bool dangerousSettings = !Reporting::IsSupported(); clearColor = dangerousSettings ? 0xFF900050 : 0xFF900000; - draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_RuntimeError"); - framebufferBound = true; + draw->Clear(Draw::Aspect::COLOR_BIT, clearColor, 0.0f, 0); // The info is drawn later in renderUI } else { // If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output. // This won't work in non-buffered, but that's fine. - if (!framebufferBound && PSP_IsInited()) { - // draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_Stepping"); + if (PSP_IsInited()) { gpu->SetCurFramebufferDirty(true); - gpu->CopyDisplayToOutput(displayLayoutConfig); - framebufferBound = true; } } break; @@ -1823,6 +1803,8 @@ ScreenRenderFlags EmuScreen::RunEmulation(ScreenRenderMode mode, bool framebuffe } if (gpu) { + // Run post processing and other passes. + gpu->PrepareCopyDisplayToOutput(displayLayoutConfig); gpu->EndHostFrame(); } @@ -1850,58 +1832,6 @@ ScreenRenderFlags EmuScreen::RunEmulation(ScreenRenderMode mode, bool framebuffe } } - if (gpu && gpu->PresentedThisFrame()) { - framebufferBound = true; - } - - if (!framebufferBound) { - draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_NoFrame"); - draw->SetViewport(viewport); - draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres); - } - - Draw::BackendState state = draw->GetCurrentBackendState(); - - // State.valid just states whether the passes parameter has a meaningful value. - if (state.valid) { - _dbg_assert_msg_(state.passes >= 1, "skipB: %d sw: %d mode: %d back: %d bound: %d", (int)skipBufferEffects, (int)g_Config.bSoftwareRendering, (int)mode, (int)g_Config.iGPUBackend, (int)framebufferBound); - if (state.passes == 0) { - // Workaround any remaining bugs like this. - draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, }, "EmuScreen_SafeFallback"); - } - } - - screenManager()->getUIContext()->BeginFrame(); - - if (!(mode & ScreenRenderMode::TOP)) { - renderImDebugger(); - // We're in run-behind mode, but we don't want to draw chat, debug UI and stuff. We do draw the imdebugger though. - // So, darken and bail here. - // Reset viewport/scissor to be sure. - draw->SetViewport(viewport); - draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres); - darken(); - return flags; - } - - // NOTE: We don't check for powerdown if we're not the top screen. - if (checkPowerDown()) { - draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_PowerDown"); - } - - if (hasVisibleUI()) { - draw->SetViewport(viewport); - cardboardDisableButton_->SetVisibility(displayLayoutConfig.bEnableCardboardVR ? UI::V_VISIBLE : UI::V_GONE); - renderUI(); - } - - if (chatMenu_ && (chatMenu_->GetVisibility() == UI::V_VISIBLE)) { - SetVRAppMode(VRAppMode::VR_DIALOG_MODE); - } else { - SetVRAppMode(screenManager()->topScreen() == this ? VRAppMode::VR_GAME_MODE : VRAppMode::VR_DIALOG_MODE); - } - - renderImDebugger(); return flags; } @@ -2017,21 +1947,12 @@ bool EmuScreen::hasVisibleUI() { void EmuScreen::renderUI() { using namespace Draw; - DrawContext *thin3d = screenManager()->getDrawContext(); + DrawContext *draw = screenManager()->getDrawContext(); UIContext *ctx = screenManager()->getUIContext(); ctx->BeginFrame(); // This sets up some important states but not the viewport. ctx->Begin(); - 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; - thin3d->SetViewport(viewport); - if (root_) { UI::LayoutViewHierarchy(*ctx, RootMargins(), root_, false, false); root_->Draw(*ctx); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 93bce1a0f1..2e87c5a452 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -70,10 +70,11 @@ public: protected: void darken(); void focusChanged(ScreenFocusChange focusChange) override; + ScreenRenderFlags PreRender() override; private: void CreateViews() override; - ScreenRenderFlags RunEmulation(ScreenRenderMode mode, bool framebufferBound, bool skipBufferEffects); + ScreenRenderFlags RunEmulation(bool skipBufferEffects); void OnDevTools(UI::EventParams ¶ms); void OnChat(UI::EventParams ¶ms); @@ -159,6 +160,7 @@ private: #endif bool autoLoadFailed_ = false; // to prevent repeat reloads bool readyToFinishBoot_ = false; + bool skipBufferEffects_ = false; // cached state, fetched once per frame. }; bool MustRunBehind(); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 0f23a9fd84..adb47454d3 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -98,12 +98,6 @@ void HandleCommonMessages(UIMessage message, const char *value, ScreenManager *m } ScreenRenderFlags BackgroundScreen::render(ScreenRenderMode mode) { - if (mode & ScreenRenderMode::FIRST) { - SetupViewport(); - } else { - _dbg_assert_(false); - } - UIContext *uiContext = screenManager()->getUIContext(); uiContext->PushTransform({ translation_, scale_, alpha_ }); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 6cbc84647e..704a596d8a 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1151,7 +1151,7 @@ void NativeFrame(GraphicsContext *graphicsContext) { g_screenManager->getUIContext()->SetTintSaturation(g_Config.fUITint, g_Config.fUISaturation); - // All actual rendering happen in here. + // All actual rendering (and also emulation) happens in here. ScreenRenderFlags renderFlags = g_screenManager->render(); if (g_screenManager->getUIContext()->Text()) { g_screenManager->getUIContext()->Text()->OncePerFrame(); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 520e84ce4c..293878d511 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -266,10 +266,11 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const } if (draw) { - draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless"); + draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Backbuffer"); // Vulkan may get angry if we don't do a final present. if (gpu) { gpu->SetCurFramebufferDirty(true); + gpu->PrepareCopyDisplayToOutput(g_Config.GetDisplayLayoutConfig(DeviceOrientation::Landscape)); gpu->CopyDisplayToOutput(g_Config.GetDisplayLayoutConfig(DeviceOrientation::Landscape)); }