From d2d8688e4799792e7674406ba0f5d377ff78d901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 24 Aug 2023 12:52:00 +0200 Subject: [PATCH] Add "Create frame dump" to the in-game developer menu (that can be enabled in dev settings) Makes it possible to create one without connecting the websocket debugger, even on non-Windows platforms. --- .../WebSocket/GPURecordSubscriber.cpp | 17 +++++++------ GPU/Debugger/Record.cpp | 12 +++++---- GPU/Debugger/Record.h | 5 ++-- UI/DevScreens.cpp | 25 ++++++++++++++++--- Windows/GEDebugger/GEDebugger.cpp | 5 ++-- assets/lang/en_US.ini | 1 + 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp index f1aad51e12..30516ba81e 100644 --- a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp @@ -44,7 +44,7 @@ DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) { WebSocketGPURecordState::~WebSocketGPURecordState() { // Clear the callback to hopefully avoid a crash. if (pending_) - GPURecord::SetCallback(nullptr); + GPURecord::ClearCallback(); } // Begin recording (gpu.record.dump) @@ -56,18 +56,21 @@ WebSocketGPURecordState::~WebSocketGPURecordState() { // // Note: recording may take a moment. void WebSocketGPURecordState::Dump(DebuggerRequest &req) { - if (!PSP_IsInited()) + if (!PSP_IsInited()) { return req.Fail("CPU not started"); + } - if (!GPURecord::Activate()) - return req.Fail("Recording already in progress"); - - pending_ = true; - GPURecord::SetCallback([=](const Path &filename) { + bool result = GPURecord::RecordNextFrame([=](const Path &filename) { lastFilename_ = filename; pending_ = false; }); + if (!result) { + return req.Fail("Recording already in progress"); + } + + pending_ = true; + const JsonNode *value = req.data.get("ticket"); lastTicket_ = value ? json_stringify(value) : ""; } diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index d1a1bbf2ca..b0cc4a5a62 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -49,7 +49,7 @@ namespace GPURecord { static bool active = false; -static bool nextFrame = false; +static std::atomic nextFrame = false; static int flipLastAction = -1; static int flipFinishAt = -1; static uint32_t lastEdramTrans = 0x400; @@ -601,18 +601,20 @@ bool IsActivePending() { return nextFrame || active; } -bool Activate() { +bool RecordNextFrame(const std::function callback) { if (!nextFrame) { - nextFrame = true; flipLastAction = gpuStats.numFlips; flipFinishAt = -1; + writeCallback = callback; + nextFrame = true; return true; } return false; } -void SetCallback(const std::function callback) { - writeCallback = callback; +void ClearCallback() { + // Not super thread safe.. + writeCallback = nullptr; } static void FinishRecording() { diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index 5c5d2f6dbc..454fc4926c 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -27,9 +27,8 @@ namespace GPURecord { bool IsActive(); bool IsActivePending(); -bool Activate(); -// Call only if Activate() returns true. -void SetCallback(const std::function callback); +bool RecordNextFrame(const std::function callback); +void ClearCallback(); void NotifyCommand(u32 pc); void NotifyMemcpy(u32 dest, u32 src, u32 sz); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 750ce9c1ee..14a026dc47 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -63,6 +63,7 @@ #include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/MIPS/JitCommon/JitState.h" +#include "GPU/Debugger/Record.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" #include "UI/MiscScreens.h" @@ -139,11 +140,27 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { return UI::EVENT_DONE; }); - items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Add([](UI::EventParams &e) { - gpu->DumpNextFrame(); + items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenuScreen::OnResetLimitedLogging); + + items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) { + GPURecord::RecordNextFrame([](const Path &dumpPath) { + NOTICE_LOG(SYSTEM, "Frame dump created at '%s'", dumpPath.c_str()); + if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { + System_ShowFileInFolder(dumpPath); + } else { + g_OSD.Show(OSDType::MESSAGE_SUCCESS, dumpPath.ToVisualString(), 7.0f); + } + }); return UI::EVENT_DONE; }); - items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenuScreen::OnResetLimitedLogging); + + // This one is not very useful these days, and only really on desktop. Hide it on other platforms. + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) { + items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Add([](UI::EventParams &e) { + gpu->DumpNextFrame(); + return UI::EVENT_DONE; + }); + } scroll->Add(items); parent->Add(scroll); @@ -1422,4 +1439,4 @@ void FrameDumpTestScreen::update() { } RecreateViews(); } -} \ No newline at end of file +} diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 5d71509b6f..15869fb242 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -1212,11 +1212,10 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case IDC_GEDBG_RECORD: - GPURecord::SetCallback([](const Path &path) { - // Opens a Windows Explorer window with the file. + GPURecord::RecordNextFrame([](const Path &path) { + // Opens a Windows Explorer window with the file, when done. System_ShowFileInFolder(path); }); - GPURecord::Activate(); break; case IDC_GEDBG_FLUSH: diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index ccae3be914..22e17db73b 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -302,6 +302,7 @@ Block address = Block address By Address = By address Control Debug = Control Debug Copy savestates to memstick root = Copy save states to Memory Stick root +Create frame dump = Create frame dump Create/Open textures.ini file for current game = Create/Open textures.ini file for current game Current = Current Dev Tools = Development tools