Finish the split. Greatly simplifies the render code in EmuScreen.

This commit is contained in:
Henrik Rydgård
2026-02-08 10:38:07 +01:00
parent 2ea94534e6
commit eb234a1563
21 changed files with 158 additions and 226 deletions
+5
View File
@@ -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);
+25 -18
View File
@@ -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_);
}
+2
View File
@@ -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;
+18
View File
@@ -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<Screen *, 6> layers;
+3 -2
View File
@@ -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);
-23
View File
@@ -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();
-1
View File
@@ -77,7 +77,6 @@ protected:
virtual void DrawBackground(UIContext &ui) {}
virtual void DrawForeground(UIContext &ui) {}
void SetupViewport();
void DoRecreateViews();
bool recreateViews_ = true;
-1
View File
@@ -662,7 +662,6 @@ void __DisplayFlip(int cyclesLate) {
}
if (nextFrame) {
gpu->SetCurFramebufferDirty(fbReallyDirty);
gpu->CopyDisplayToOutput(g_displayLayoutConfigCached);
if (fbReallyDirty) {
DisplayFireActualFlip();
}
+2 -4
View File
@@ -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;
}
-1
View File
@@ -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_) {
+1
View File
@@ -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;
+4 -1
View File
@@ -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;
}
+1
View File
@@ -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;
+6 -3
View File
@@ -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);
}
+1
View File
@@ -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<const VirtualFramebuffer *> GetFramebufferList() const override { return std::vector<const VirtualFramebuffer *>(); }
+1 -1
View File
@@ -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 {
+83 -162
View File
@@ -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);
+3 -1
View File
@@ -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 &params);
void OnChat(UI::EventParams &params);
@@ -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();
-6
View File
@@ -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_ });
+1 -1
View File
@@ -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();
+2 -1
View File
@@ -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));
}