From ba55ee7e5f0e1520eef698ea96c6f1d863741c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 23:07:10 +0200 Subject: [PATCH] Screenshots: Take the game image while the game is on a TV An output screenshot reads back the device's own screen, which only has the touch controls on it then. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/Screenshot.cpp | 3 +++ GPU/Common/FramebufferManagerCommon.cpp | 4 ++++ GPU/Common/FramebufferManagerCommon.h | 1 + GPU/Common/PresentationCommon.h | 3 +++ GPU/GPUCommon.h | 2 ++ GPU/GPUCommonHW.cpp | 4 ++++ GPU/GPUCommonHW.h | 1 + GPU/Software/SoftGpu.cpp | 4 ++++ GPU/Software/SoftGpu.h | 1 + 9 files changed, 23 insertions(+) diff --git a/Core/Screenshot.cpp b/Core/Screenshot.cpp index 5572fba0d8..5401c55d05 100644 --- a/Core/Screenshot.cpp +++ b/Core/Screenshot.cpp @@ -526,6 +526,9 @@ void TakeUserScreenshot() { if (GetUIState() != UISTATE_INGAME) { // We're out in the UI, no game. The only type of screenshot available is output. type = ScreenshotType::Output; + } else if (gpu && gpu->PresentsToOtherOutput()) { + // The game is on a TV. The device's own screen only has the controls on it. + type = ScreenshotType::Display; } // Close any active popups, so they don't end up in the screenshot. diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index ae684a1028..62ad201f37 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -132,6 +132,10 @@ void FramebufferManagerCommon::SetPresentationOutput(const OutputParams *output) presentation_->SetOutputOverride(output); } +bool FramebufferManagerCommon::PresentsToOtherOutput() const { + return presentation_->HasOutputOverride(); +} + void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { displayFramebufPtr_ = framebuf & 0x3FFFFFFF; if (Memory::IsVRAMAddress(displayFramebufPtr_)) diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 61e2d4b250..6b7b53148d 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -525,6 +525,7 @@ public: bool PresentedThisFrame() const; void SetPresentationOutput(const OutputParams *output); + bool PresentsToOtherOutput() const; const std::vector &GetVFBs() const { return vfbs_; diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index 1d204af3a3..e3cab6d581 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -129,6 +129,9 @@ public: restorePostShader_ = true; } } + bool HasOutputOverride() const { + return hasOutputOverride_; + } OutputParams GetOutput() const { return hasOutputOverride_ ? outputOverride_ : PrimaryOutputParams(pixelWidth_, pixelHeight_); } diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index c63a415b7c..b8f71d7d51 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -156,6 +156,8 @@ public: // Presents the display to another output than the primary one, until called with nullptr. Set it // before PrepareCopyDisplayToOutput, which lays out for it. virtual void SetPresentationOutput(const OutputParams *output) {} + // Whether the game goes to another output than the primary (SetPresentationOutput), like a TV. + virtual bool PresentsToOtherOutput() const { return false; } // Invalidate any cached content sourced from the specified range. // If size = -1, invalidate everything. diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 6a00b9291b..446e9869db 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -525,6 +525,10 @@ void GPUCommonHW::SetPresentationOutput(const OutputParams *output) { framebufferManager_->SetPresentationOutput(output); } +bool GPUCommonHW::PresentsToOtherOutput() const { + return framebufferManager_->PresentsToOtherOutput(); +} + void GPUCommonHW::DoState(PointerWrap &p) { GPUCommon::DoState(p); diff --git a/GPU/GPUCommonHW.h b/GPU/GPUCommonHW.h index e4f3f0d9a8..3ffc78fbf7 100644 --- a/GPU/GPUCommonHW.h +++ b/GPU/GPUCommonHW.h @@ -72,6 +72,7 @@ public: bool PresentedThisFrame() const override; void SetPresentationOutput(const OutputParams *output) override; + bool PresentsToOtherOutput() const override; private: void CheckDepthUsage(VirtualFramebuffer *vfb) override; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 8293e46dca..abc31c7afb 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -726,6 +726,10 @@ void SoftGPU::SetPresentationOutput(const OutputParams *output) { } } +bool SoftGPU::PresentsToOtherOutput() const { + return presentation_ && presentation_->HasOutputOverride(); +} + void SoftGPU::MarkDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value) { uint32_t bytes = height * stride * (fmt == GE_FORMAT_8888 ? 4 : 2); MarkDirty(addr, bytes, value); diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 79e1fba66c..4661faeb99 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -257,6 +257,7 @@ public: void BeginHostFrame(const DisplayLayoutConfig &config) override; bool PresentedThisFrame() const override; void SetPresentationOutput(const OutputParams *output) override; + bool PresentsToOtherOutput() const override; protected: void FastRunLoop(DisplayList &list) override;