Debugger: A screenshot of a display that's off is black

gpu.buffer.screenshot failed with "Could not download output" while a game had the display off
(sceDisplaySetFrameBuf with 0) and there was no backbuffer to read instead, as in headless Vulkan.
Pangya: Fantasy Golf does that around its loading screens, which ended the game scripts waiting
for its next screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-31 19:02:12 +01:00
1 parent 3626497c76
commit 1815cf065e
1 file changed
+13 -1
@@ -22,6 +22,8 @@
#include "Common/StringUtils.h"
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/HLE/sceDisplay.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/Screenshot.h"
#include "GPU/Debugger/Stepping.h"
@@ -255,7 +257,7 @@ static void GenericStreamBuffer(DebuggerRequest &req, std::function<bool(const G
}
}
// Retrieve a screenshot (gpu.buffer.screenshot)
// Retrieve a screenshot (gpu.buffer.screenshot). While the game has the display off, it's black.
//
// Parameters:
// - type: either 'uri' or 'base64' (optional, defaults to 'uri').
@@ -282,6 +284,16 @@ void WebSocketGPUBufferScreenshot(DebuggerRequest &req) {
return true;
}
// Without a backbuffer (headless Vulkan), the displayed PSP framebuffer is the closest thing.
PSPPointer<u8> displayAddr;
__DisplayGetFramebuf(&displayAddr, nullptr, nullptr, 0);
if (!Memory::IsValidAddress(displayAddr.ptr)) {
// The game has turned the display off (sceDisplaySetFrameBuf with 0), so the screen is black.
static GPUDebugBuffer black;
black.Allocate(480, 272, GE_FORMAT_8888);
memset(black.GetData(), 0, 480 * 272 * 4);
buf = &black;
return true;
}
*isFramebuffer = true;
return GPUStepping::GPU_GetCurrentFramebuffer(buf, GPU_DBG_FRAMEBUF_DISPLAY);
});