diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff09b4c9bd..2f9a21e4f4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1357,6 +1357,8 @@ set(GPU_SOURCES
GPU/Common/SplineCommon.h
GPU/Debugger/Breakpoints.cpp
GPU/Debugger/Breakpoints.h
+ GPU/Debugger/Debugger.cpp
+ GPU/Debugger/Debugger.h
GPU/Debugger/Record.cpp
GPU/Debugger/Record.h
GPU/Debugger/Stepping.cpp
@@ -1430,6 +1432,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/GameSubscriber.h
Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
Core/Debugger/WebSocket/GPUBufferSubscriber.h
+ Core/Debugger/WebSocket/GPURecordSubscriber.cpp
+ Core/Debugger/WebSocket/GPURecordSubscriber.h
Core/Debugger/WebSocket/HLESubscriber.cpp
Core/Debugger/WebSocket/HLESubscriber.h
Core/Debugger/WebSocket/LogBroadcaster.cpp
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 068241f0c6..c5a39b54be 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -188,6 +188,7 @@
+
@@ -549,6 +550,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index ba79121304..b459851f73 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -734,6 +734,9 @@
HLE\Libraries
+
+ Debugger\WebSocket
+
@@ -1361,6 +1364,9 @@
HLE\Libraries
+
+ Debugger\WebSocket
+
diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp
index 4504c827f7..6175279363 100644
--- a/Core/Debugger/WebSocket.cpp
+++ b/Core/Debugger/WebSocket.cpp
@@ -53,24 +53,20 @@
#include "Core/Debugger/WebSocket/DisasmSubscriber.h"
#include "Core/Debugger/WebSocket/GameSubscriber.h"
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
+#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
#include "Core/Debugger/WebSocket/HLESubscriber.h"
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"
-typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map);
-typedef void (*Subscribershutdown)(void *p);
-struct SubscriberInfo {
- SubscriberInit init;
- Subscribershutdown shutdown;
-};
-
-static const std::vector subscribers({
- { &WebSocketBreakpointInit, nullptr },
- { &WebSocketCPUCoreInit, nullptr },
- { &WebSocketDisasmInit, &WebSocketDisasmShutdown },
- { &WebSocketGameInit, nullptr },
- { &WebSocketGPUBufferInit, nullptr },
- { &WebSocketHLEInit, nullptr },
- { &WebSocketSteppingInit, &WebSocketSteppingShutdown },
+typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map);
+static const std::vector subscribers({
+ &WebSocketBreakpointInit,
+ &WebSocketCPUCoreInit,
+ &WebSocketDisasmInit,
+ &WebSocketGameInit,
+ &WebSocketGPUBufferInit,
+ &WebSocketGPURecordInit,
+ &WebSocketHLEInit,
+ &WebSocketSteppingInit,
});
// To handle webserver restart, keep track of how many running.
@@ -132,10 +128,10 @@ void HandleDebuggerRequest(const http::Request &request) {
SteppingBroadcaster stepping;
std::unordered_map eventHandlers;
- std::vector subscriberData;
- for (auto info : subscribers) {
+ std::vector subscriberData;
+ for (auto init : subscribers) {
std::lock_guard guard(lifecycleLock);
- subscriberData.push_back(info.init(eventHandlers));
+ subscriberData.push_back(init(eventHandlers));
}
// There's a tradeoff between responsiveness to incoming events, and polling for changes.
@@ -178,6 +174,12 @@ void HandleDebuggerRequest(const http::Request &request) {
game.Broadcast(ws);
stepping.Broadcast(ws);
+ for (size_t i = 0; i < subscribers.size(); ++i) {
+ if (subscriberData[i]) {
+ subscriberData[i]->Broadcast(ws);
+ }
+ }
+
if (stopRequested) {
ws->Close(net::WebSocketClose::GOING_AWAY);
}
@@ -188,11 +190,7 @@ void HandleDebuggerRequest(const http::Request &request) {
std::lock_guard guard(lifecycleLock);
for (size_t i = 0; i < subscribers.size(); ++i) {
- if (subscribers[i].shutdown) {
- subscribers[i].shutdown(subscriberData[i]);
- } else {
- assert(!subscriberData[i]);
- }
+ delete subscriberData[i];
}
delete ws;
diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp
index ffe1797ce5..aab5bf8346 100644
--- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp
+++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp
@@ -23,7 +23,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MIPS/MIPSDebugInterface.h"
-void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd;
map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate;
diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h
index c9c2e1effc..d0e622cd12 100644
--- a/Core/Debugger/WebSocket/BreakpointSubscriber.h
+++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
+DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
void WebSocketCPUBreakpointAdd(DebuggerRequest &req);
void WebSocketCPUBreakpointUpdate(DebuggerRequest &req);
diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
index 0d2c42f66c..6f371e5b85 100644
--- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
+++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
@@ -25,7 +25,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSDebugInterface.h"
-void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.stepping"] = &WebSocketCPUStepping;
map["cpu.resume"] = &WebSocketCPUResume;
diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h
index 3667979e96..99dc53adaa 100644
--- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h
+++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
+DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
void WebSocketCPUStepping(DebuggerRequest &req);
void WebSocketCPUResume(DebuggerRequest &req);
diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp
index 78ee607fdf..6d2f76d746 100644
--- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp
+++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp
@@ -28,11 +28,12 @@
#include "Core/MIPS/MIPSAsm.h"
#include "Core/MIPS/MIPSDebugInterface.h"
-struct WebSocketDisasmState {
+class WebSocketDisasmState : public DebuggerSubscriber {
+public:
WebSocketDisasmState() {
disasm_.setCpu(currentDebugMIPS);
}
- ~WebSocketDisasmState() {
+ ~WebSocketDisasmState() override {
disasm_.clear();
}
@@ -49,7 +50,7 @@ protected:
DisassemblyManager disasm_;
};
-void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketDisasmState();
map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1);
map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1);
@@ -59,10 +60,6 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
return p;
}
-void WebSocketDisasmShutdown(void *p) {
- delete static_cast(p);
-}
-
static DebugInterface *CPUFromRequest(DebuggerRequest &req) {
if (!req.HasParam("thread"))
return currentDebugMIPS;
diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.h b/Core/Debugger/WebSocket/DisasmSubscriber.h
index e6c5b0e71c..35a32fa2bb 100644
--- a/Core/Debugger/WebSocket/DisasmSubscriber.h
+++ b/Core/Debugger/WebSocket/DisasmSubscriber.h
@@ -19,5 +19,4 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
-void WebSocketDisasmShutdown(void *p);
+DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
index 98f65231c2..67b13d85b2 100644
--- a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
+++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
@@ -28,7 +28,7 @@
#include "Core/Screenshot.h"
#include "GPU/Debugger/Stepping.h"
-void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["gpu.buffer.screenshot"] = &WebSocketGPUBufferScreenshot;
map["gpu.buffer.renderColor"] = &WebSocketGPUBufferRenderColor;
diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.h b/Core/Debugger/WebSocket/GPUBufferSubscriber.h
index c7062fdb82..00b93d74c0 100644
--- a/Core/Debugger/WebSocket/GPUBufferSubscriber.h
+++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.h
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
+DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
void WebSocketGPUBufferScreenshot(DebuggerRequest &req);
void WebSocketGPUBufferRenderColor(DebuggerRequest &req);
diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp
new file mode 100644
index 0000000000..961a4d0d04
--- /dev/null
+++ b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2018- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "data/base64.h"
+#include "Common/FileUtil.h"
+#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
+#include "Core/Debugger/WebSocket/WebSocketUtils.h"
+#include "Core/System.h"
+#include "GPU/Debugger/Record.h"
+
+struct WebSocketGPURecordState : public DebuggerSubscriber {
+ ~WebSocketGPURecordState() override;
+ void Dump(DebuggerRequest &req);
+
+ void Broadcast(net::WebSocketServer *ws) override;
+
+protected:
+ bool pending_ = false;
+ std::string lastTicket_;
+ std::string lastFilename_;
+};
+
+DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) {
+ auto p = new WebSocketGPURecordState();
+ map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1);
+
+ return p;
+}
+
+WebSocketGPURecordState::~WebSocketGPURecordState() {
+ // Clear the callback to hopefully avoid a crash.
+ if (pending_)
+ GPURecord::SetCallback(nullptr);
+}
+
+// Begin recording (gpu.record.dump)
+//
+// No parameters.
+//
+// Response (same event name):
+// - uri: data: URI containing debug dump data.
+//
+// Note: recording may take a moment.
+void WebSocketGPURecordState::Dump(DebuggerRequest &req) {
+ if (!PSP_IsInited())
+ return req.Fail("CPU not started");
+
+ if (!GPURecord::Activate())
+ return req.Fail("Recording already in progress");
+
+ pending_ = true;
+ GPURecord::SetCallback([=](const std::string &filename) {
+ lastFilename_ = filename;
+ pending_ = false;
+ });
+
+ const JsonNode *value = req.data.get("ticket");
+ lastTicket_ = value ? json_stringify(value) : "";
+}
+
+// This handles the asynchronous gpu.record.dump response.
+void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) {
+ if (!lastFilename_.empty()) {
+ FILE *fp = File::OpenCFile(lastFilename_, "rb");
+ if (!fp) {
+ lastFilename_.clear();
+ return;
+ }
+
+ // We write directly to the stream since this is a large chunk of data.
+ ws->AddFragment(false, R"({"event":"gpu.record.dump")");
+ if (!lastTicket_.empty()) {
+ ws->AddFragment(false, R"(,"ticket":)");
+ ws->AddFragment(false, lastTicket_);
+ }
+ ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)");
+
+ // Divisible by 3 for base64 reasons.
+ const size_t BUF_SIZE = 16383;
+ std::vector buf;
+ buf.resize(BUF_SIZE);
+ while (!feof(fp)) {
+ size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp);
+ ws->AddFragment(false, Base64Encode(&buf[0], bytes));
+ }
+ fclose(fp);
+
+ ws->AddFragment(true, R"("})");
+
+ lastFilename_.clear();
+ lastTicket_.clear();
+ }
+}
diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.h b/Core/Debugger/WebSocket/GPURecordSubscriber.h
new file mode 100644
index 0000000000..31a1f6b42d
--- /dev/null
+++ b/Core/Debugger/WebSocket/GPURecordSubscriber.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2018- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include "Core/Debugger/WebSocket/WebSocketUtils.h"
+
+DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map);
diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp
index a17717e262..0acd4a9b3e 100644
--- a/Core/Debugger/WebSocket/GameSubscriber.cpp
+++ b/Core/Debugger/WebSocket/GameSubscriber.cpp
@@ -20,7 +20,7 @@
#include "Core/ELF/ParamSFO.h"
#include "Core/System.h"
-void *WebSocketGameInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) {
map["game.status"] = &WebSocketGameStatus;
map["version"] = &WebSocketVersion;
diff --git a/Core/Debugger/WebSocket/GameSubscriber.h b/Core/Debugger/WebSocket/GameSubscriber.h
index 77cec9e38b..e3d08f4522 100644
--- a/Core/Debugger/WebSocket/GameSubscriber.h
+++ b/Core/Debugger/WebSocket/GameSubscriber.h
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketGameInit(DebuggerEventHandlerMap &map);
+DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map);
void WebSocketGameStatus(DebuggerRequest &req);
void WebSocketVersion(DebuggerRequest &req);
diff --git a/Core/Debugger/WebSocket/HLESubscriber.cpp b/Core/Debugger/WebSocket/HLESubscriber.cpp
index 586f30199c..ae65e692fa 100644
--- a/Core/Debugger/WebSocket/HLESubscriber.cpp
+++ b/Core/Debugger/WebSocket/HLESubscriber.cpp
@@ -27,7 +27,7 @@
#include "Core/MIPS/MIPSStackWalk.h"
#include "Core/HLE/sceKernelThread.h"
-void *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
map["hle.thread.list"] = &WebSocketHLEThreadList;
map["hle.thread.wake"] = &WebSocketHLEThreadWake;
map["hle.thread.stop"] = &WebSocketHLEThreadStop;
diff --git a/Core/Debugger/WebSocket/HLESubscriber.h b/Core/Debugger/WebSocket/HLESubscriber.h
index 9b059f4759..6d473c3563 100644
--- a/Core/Debugger/WebSocket/HLESubscriber.h
+++ b/Core/Debugger/WebSocket/HLESubscriber.h
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketHLEInit(DebuggerEventHandlerMap &map);
+DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map);
void WebSocketHLEThreadList(DebuggerRequest &req);
void WebSocketHLEThreadWake(DebuggerRequest &req);
diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp
index e1347f3a14..d3422b48f9 100644
--- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp
+++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp
@@ -28,11 +28,11 @@
using namespace MIPSAnalyst;
-struct WebSocketSteppingState {
+struct WebSocketSteppingState : public DebuggerSubscriber {
WebSocketSteppingState() {
disasm_.setCpu(currentDebugMIPS);
}
- ~WebSocketSteppingState() {
+ ~WebSocketSteppingState() override {
disasm_.clear();
}
@@ -51,7 +51,7 @@ protected:
DisassemblyManager disasm_;
};
-void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
+DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketSteppingState();
map["cpu.stepInto"] = std::bind(&WebSocketSteppingState::Into, p, std::placeholders::_1);
map["cpu.stepOver"] = std::bind(&WebSocketSteppingState::Over, p, std::placeholders::_1);
@@ -62,10 +62,6 @@ void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
return p;
}
-void WebSocketSteppingShutdown(void *p) {
- delete static_cast(p);
-}
-
static DebugInterface *CPUFromRequest(DebuggerRequest &req, uint32_t *threadID = nullptr) {
if (!req.HasParam("thread")) {
if (threadID)
diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.h b/Core/Debugger/WebSocket/SteppingSubscriber.h
index 319c608caa..06983ea278 100644
--- a/Core/Debugger/WebSocket/SteppingSubscriber.h
+++ b/Core/Debugger/WebSocket/SteppingSubscriber.h
@@ -19,5 +19,4 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
-void *WebSocketSteppingInit(DebuggerEventHandlerMap &map);
-void WebSocketSteppingShutdown(void *p);
+DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map);
diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h
index 70c08828b2..85dca3f4a8 100644
--- a/Core/Debugger/WebSocket/WebSocketUtils.h
+++ b/Core/Debugger/WebSocket/WebSocketUtils.h
@@ -98,5 +98,13 @@ private:
bool responsePartial_ = false;
};
+class DebuggerSubscriber {
+public:
+ virtual ~DebuggerSubscriber() {}
+
+ // Subscribers can also broadcast if they have simple cases to.
+ virtual void Broadcast(net::WebSocketServer *ws) {}
+};
+
typedef std::function DebuggerEventHandler;
typedef std::unordered_map DebuggerEventHandlerMap;
diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp
index b86d467df8..02bbc1e078 100644
--- a/Core/HLE/sceDisplay.cpp
+++ b/Core/HLE/sceDisplay.cpp
@@ -55,6 +55,7 @@
#include "GPU/GPUInterface.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Common/PostShader.h"
+#include "GPU/Debugger/Record.h"
struct FrameBufferState {
u32 topaddr;
@@ -737,7 +738,7 @@ void __DisplayFlip(int cyclesLate) {
// 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
maxFrameskip = g_Config.iFrameSkip;
}
- if (numSkippedFrames >= maxFrameskip) {
+ if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) {
skipFrame = false;
}
diff --git a/Core/Host.h b/Core/Host.h
index b06473c4dc..2fd79e9510 100644
--- a/Core/Host.h
+++ b/Core/Host.h
@@ -50,13 +50,6 @@ public:
virtual void SaveSymbolMap() {}
virtual void SetWindowTitle(const char *message) {}
- // While debugging is active, it's perfectly fine for these to block.
- virtual bool GPUDebuggingActive() { return false; }
- virtual void GPUNotifyCommand(u32 pc) {}
- virtual void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {}
- virtual void GPUNotifyDraw() {}
- virtual void GPUNotifyTextureAttachment(u32 addr) {}
-
virtual bool CanCreateShortcut() {return false;}
virtual bool CreateDesktopShortcut(std::string argumentPath, std::string title) {return false;}
diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp
index 171c0bb297..1409a6b403 100644
--- a/GPU/Common/TextureCacheCommon.cpp
+++ b/GPU/Common/TextureCacheCommon.cpp
@@ -20,7 +20,6 @@
#include "Common/ColorConv.h"
#include "Common/MemoryUtil.h"
#include "Core/Config.h"
-#include "Core/Host.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "GPU/Common/FramebufferCommon.h"
@@ -28,6 +27,7 @@
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Common/GPUStateUtils.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/GPUState.h"
#include "GPU/GPUInterface.h"
@@ -708,7 +708,7 @@ void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFra
entry->maxLevel = 0;
fbTexInfo_[cachekey] = fbInfo;
framebuffer->last_frame_attached = gpuStats.numFlips;
- host->GPUNotifyTextureAttachment(entry->addr);
+ GPUDebug::NotifyTextureAttachment(entry->addr);
} else if (entry->framebuffer == framebuffer) {
framebuffer->last_frame_attached = gpuStats.numFlips;
}
@@ -727,7 +727,7 @@ void TextureCacheCommon::AttachFramebufferInvalid(TexCacheEntry *entry, VirtualF
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
entry->maxLevel = 0;
fbTexInfo_[cachekey] = fbInfo;
- host->GPUNotifyTextureAttachment(entry->addr);
+ GPUDebug::NotifyTextureAttachment(entry->addr);
}
}
@@ -740,7 +740,7 @@ void TextureCacheCommon::DetachFramebuffer(TexCacheEntry *entry, u32 address, Vi
// Otherwise we never recreate the texture.
entry->hash ^= 1;
fbTexInfo_.erase(cachekey);
- host->GPUNotifyTextureAttachment(entry->addr);
+ GPUDebug::NotifyTextureAttachment(entry->addr);
}
}
diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp
index fb7b001b7b..eac5cf078f 100644
--- a/GPU/D3D11/DrawEngineD3D11.cpp
+++ b/GPU/D3D11/DrawEngineD3D11.cpp
@@ -22,7 +22,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
-#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@@ -34,10 +33,10 @@
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/SplineCommon.h"
-
#include "GPU/Common/TransformCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/D3D11/FramebufferManagerD3D11.h"
#include "GPU/D3D11/TextureCacheD3D11.h"
#include "GPU/D3D11/DrawEngineD3D11.h"
@@ -690,10 +689,7 @@ rotateVBO:
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
-#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
- // We only support GPU debugging on Windows, and that's the only use case for this.
- host->GPUNotifyDraw();
-#endif
+ GPUDebug::NotifyDraw();
}
void DrawEngineD3D11::TessellationDataTransferD3D11::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) {
diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp
index 8ad9ebd11a..a23eb2059f 100644
--- a/GPU/D3D11/GPU_D3D11.cpp
+++ b/GPU/D3D11/GPU_D3D11.cpp
@@ -45,7 +45,6 @@
#include "Core/Debugger/Breakpoints.h"
#include "Core/MemMapHelpers.h"
#include "Core/MIPS/MIPS.h"
-#include "Core/Host.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/System.h"
@@ -55,6 +54,7 @@
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/D3D11/ShaderManagerD3D11.h"
#include "GPU/D3D11/GPU_D3D11.h"
#include "GPU/D3D11/FramebufferManagerD3D11.h"
@@ -254,7 +254,7 @@ void GPU_D3D11::BeginFrame() {
void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
// TODO: Some games like Spongebob - Yellow Avenger, never change framebuffer, they blit to it.
// So breaking on frames doesn't work. Might want to move this to sceDisplay vsync.
- host->GPUNotifyDisplay(framebuf, stride, format);
+ GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format);
}
diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp
new file mode 100644
index 0000000000..2f00c804d1
--- /dev/null
+++ b/GPU/Debugger/Debugger.cpp
@@ -0,0 +1,100 @@
+// Copyright (c) 2018- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "GPU/GPU.h"
+#include "GPU/Debugger/Breakpoints.h"
+#include "GPU/Debugger/Debugger.h"
+#include "GPU/Debugger/Stepping.h"
+
+namespace GPUDebug {
+
+static bool active = false;
+static bool inited = false;
+static BreakNext breakNext = BreakNext::NONE;
+
+static void Init() {
+ if (!inited) {
+ GPUBreakpoints::Init();
+ Core_ListenStopRequest(&GPUStepping::ForceUnpause);
+ inited = true;
+ }
+}
+
+void SetActive(bool flag) {
+ Init();
+
+ active = flag;
+ if (!active) {
+ breakNext = BreakNext::NONE;
+ GPUStepping::ResumeFromStepping();
+ }
+}
+
+bool IsActive() {
+ return active;
+}
+
+void SetBreakNext(BreakNext next) {
+ SetActive(true);
+ breakNext = next;
+ if (next == BreakNext::TEX) {
+ GPUBreakpoints::AddTextureChangeTempBreakpoint();
+ } else if (next == BreakNext::PRIM) {
+ GPUBreakpoints::AddCmdBreakpoint(GE_CMD_PRIM, true);
+ GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true);
+ GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true);
+ }
+ GPUStepping::ResumeFromStepping();
+}
+
+void NotifyCommand(u32 pc) {
+ if (!active)
+ return;
+ u32 op = Memory::ReadUnchecked_U32(pc);
+ if (breakNext == BreakNext::OP || GPUBreakpoints::IsBreakpoint(pc, op)) {
+ GPUBreakpoints::ClearTempBreakpoints();
+
+ auto info = gpuDebug->DissassembleOp(pc);
+ NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str());
+ GPUStepping::EnterStepping();
+ }
+}
+
+void NotifyDraw() {
+ if (!active)
+ return;
+ if (breakNext == BreakNext::DRAW) {
+ NOTICE_LOG(G3D, "Waiting at a draw");
+ GPUStepping::EnterStepping();
+ }
+}
+
+void NotifyDisplay(u32 framebuf, u32 stride, int format) {
+ if (!active)
+ return;
+ if (breakNext == BreakNext::FRAME) {
+ // This should work fine, start stepping at the first op of the new frame.
+ breakNext = BreakNext::OP;
+ }
+}
+
+void NotifyTextureAttachment(u32 texaddr) {
+ if (!active)
+ return;
+}
+
+}
diff --git a/GPU/Debugger/Debugger.h b/GPU/Debugger/Debugger.h
new file mode 100644
index 0000000000..caf1e8f37f
--- /dev/null
+++ b/GPU/Debugger/Debugger.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2018- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include "Common/CommonTypes.h"
+
+namespace GPUDebug {
+
+enum class BreakNext {
+ NONE,
+ OP,
+ DRAW,
+ TEX,
+ NONTEX,
+ FRAME,
+ PRIM,
+};
+
+void SetActive(bool flag);
+bool IsActive();
+
+void SetBreakNext(BreakNext next);
+
+// While debugging is active, these may block.
+void NotifyCommand(u32 pc);
+void NotifyDraw();
+void NotifyDisplay(u32 framebuf, u32 stride, int format);
+void NotifyTextureAttachment(u32 texaddr);
+
+}
diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp
index fb130ef34a..1fa237a600 100644
--- a/GPU/Debugger/Record.cpp
+++ b/GPU/Debugger/Record.cpp
@@ -48,6 +48,7 @@ static const int VERSION = 2;
static bool active = false;
static bool nextFrame = false;
static bool writePending = false;
+static std::function writeCallback;
enum class CommandType : u8 {
INIT = 0,
@@ -409,7 +410,7 @@ static void WriteCompressed(FILE *fp, const void *p, size_t sz) {
delete [] compressed;
}
-static void WriteRecording() {
+static std::string WriteRecording() {
FlushRegisters();
EmitDisplayBuf();
@@ -430,6 +431,8 @@ static void WriteRecording() {
WriteCompressed(fp, pushbuf.data(), bufsz);
fclose(fp);
+
+ return filename;
}
static void GetVertDataSizes(int vcount, const void *indices, u32 &vbytes, u32 &ibytes) {
@@ -637,8 +640,35 @@ bool IsActive() {
return active;
}
-void Activate() {
- nextFrame = true;
+bool IsActivePending() {
+ return nextFrame || active;
+}
+
+bool Activate() {
+ if (!nextFrame) {
+ nextFrame = true;
+ return true;
+ }
+ return false;
+}
+
+void SetCallback(const std::function callback) {
+ writeCallback = callback;
+}
+
+static void FinishRecording() {
+ // We're done - this was just to write the result out.
+ std::string filename = WriteRecording();
+ commands.clear();
+ pushbuf.clear();
+
+ NOTICE_LOG(SYSTEM, "Recording finished");
+ writePending = false;
+ active = false;
+
+ if (writeCallback)
+ writeCallback(filename);
+ writeCallback = nullptr;
}
void NotifyCommand(u32 pc) {
@@ -646,14 +676,7 @@ void NotifyCommand(u32 pc) {
return;
}
if (writePending) {
- WriteRecording();
- commands.clear();
- pushbuf.clear();
-
- writePending = false;
- // We're done - this was just to write the result out.
- NOTICE_LOG(SYSTEM, "Recording finished");
- active = false;
+ FinishRecording();
return;
}
@@ -749,12 +772,12 @@ void NotifyUpload(u32 dest, u32 sz) {
}
void NotifyFrame() {
- if (active && !writePending) {
+ if (active && !writePending && !commands.empty()) {
// Delay write until the first command of the next frame, so we get the right display buf.
NOTICE_LOG(SYSTEM, "Recording complete - waiting to get display buffer");
writePending = true;
}
- if (nextFrame) {
+ if (nextFrame && (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) {
NOTICE_LOG(SYSTEM, "Recording starting...");
active = true;
nextFrame = false;
diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h
index 3cfc995a80..230b361d19 100644
--- a/GPU/Debugger/Record.h
+++ b/GPU/Debugger/Record.h
@@ -17,13 +17,17 @@
#pragma once
+#include
#include
#include "Common/CommonTypes.h"
namespace GPURecord {
bool IsActive();
-void Activate();
+bool IsActivePending();
+bool Activate();
+// Call only if Activate() returns true.
+void SetCallback(const std::function callback);
void NotifyCommand(u32 pc);
void NotifyMemcpy(u32 dest, u32 src, u32 sz);
diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp
index 644d833750..b277496556 100644
--- a/GPU/Debugger/Stepping.cpp
+++ b/GPU/Debugger/Stepping.cpp
@@ -38,6 +38,7 @@ enum PauseAction {
};
static bool isStepping;
+static int stepCounter = 0;
static std::mutex pauseLock;
static std::condition_variable pauseWait;
@@ -149,7 +150,7 @@ bool SingleStep() {
return true;
}
-bool EnterStepping(std::function callback) {
+bool EnterStepping() {
std::unique_lock guard(pauseLock);
if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) {
// Shutting down, don't try to step.
@@ -170,8 +171,7 @@ bool EnterStepping(std::function callback) {
pauseAction = PAUSE_BREAK;
}
isStepping = true;
-
- callback();
+ stepCounter++;
do {
RunPauseAction();
@@ -187,6 +187,10 @@ bool IsStepping() {
return isStepping;
}
+int GetSteppingCounter() {
+ return stepCounter;
+}
+
static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) {
if (!isStepping && coreState != CORE_STEPPING) {
return false;
diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h
index fb24cf072a..85b8a1082e 100644
--- a/GPU/Debugger/Stepping.h
+++ b/GPU/Debugger/Stepping.h
@@ -24,12 +24,12 @@
#include "GPU/Common/GPUDebugInterface.h"
namespace GPUStepping {
- // Should be called from the GPU thread.
- // Begins stepping and calls callback while inside a lock preparing stepping.
- // This would be a good place to deliver a message to code that stepping is ready.
- bool EnterStepping(std::function callback);
+ // Should be called from the emu thread.
+ // Begins stepping and increments the stepping counter while inside a lock.
+ bool EnterStepping();
bool SingleStep();
bool IsStepping();
+ int GetSteppingCounter();
bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer);
bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type);
diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp
index de1a85d093..e4f24703ee 100644
--- a/GPU/Directx9/DrawEngineDX9.cpp
+++ b/GPU/Directx9/DrawEngineDX9.cpp
@@ -20,7 +20,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
-#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@@ -36,6 +35,7 @@
#include "GPU/Common/TransformCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Directx9/TextureCacheDX9.h"
#include "GPU/Directx9/DrawEngineDX9.h"
#include "GPU/Directx9/ShaderManagerDX9.h"
@@ -621,7 +621,7 @@ rotateVBO:
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
- host->GPUNotifyDraw();
+ GPUDebug::NotifyDraw();
}
void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords)
diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp
index afd5a86d10..154a5e44d6 100644
--- a/GPU/Directx9/GPU_DX9.cpp
+++ b/GPU/Directx9/GPU_DX9.cpp
@@ -39,6 +39,7 @@
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Directx9/ShaderManagerDX9.h"
#include "GPU/Directx9/GPU_DX9.h"
#include "GPU/Directx9/FramebufferDX9.h"
@@ -230,7 +231,7 @@ void GPU_DX9::BeginFrame() {
}
void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
- host->GPUNotifyDisplay(framebuf, stride, format);
+ GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManagerDX9_->SetDisplayFramebuffer(framebuf, stride, format);
}
diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp
index 3880edc83b..a8857b17bc 100644
--- a/GPU/GLES/DrawEngineGLES.cpp
+++ b/GPU/GLES/DrawEngineGLES.cpp
@@ -20,7 +20,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
-#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@@ -37,6 +36,7 @@
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/GLES/FragmentTestCacheGLES.h"
#include "GPU/GLES/StateMappingGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
@@ -648,9 +648,7 @@ rotateVBO:
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
-#ifndef MOBILE_DEVICE
- host->GPUNotifyDraw();
-#endif
+ GPUDebug::NotifyDraw();
}
bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {
diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp
index 5eca0e9296..2a75ead250 100644
--- a/GPU/GLES/GPU_GLES.cpp
+++ b/GPU/GLES/GPU_GLES.cpp
@@ -35,7 +35,7 @@
#include "GPU/ge_constants.h"
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
-
+#include "GPU/Debugger/Debugger.h"
#include "GPU/GLES/ShaderManagerGLES.h"
#include "GPU/GLES/GPU_GLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
@@ -443,7 +443,7 @@ void GPU_GLES::BeginFrame() {
}
void GPU_GLES::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
- host->GPUNotifyDisplay(framebuf, stride, format);
+ GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManagerGL_->SetDisplayFramebuffer(framebuf, stride, format);
}
diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj
index 296a2081b0..b531442bb8 100644
--- a/GPU/GPU.vcxproj
+++ b/GPU/GPU.vcxproj
@@ -217,6 +217,7 @@
+
@@ -320,6 +321,7 @@
+
diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters
index 7dc2f25cad..1710c3f790 100644
--- a/GPU/GPU.vcxproj.filters
+++ b/GPU/GPU.vcxproj.filters
@@ -273,6 +273,9 @@
Vulkan
+
+ Debugger
+
@@ -542,5 +545,8 @@
Vulkan
+
+ Debugger
+
\ No newline at end of file
diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp
index 69d733835b..fed71166ae 100644
--- a/GPU/GPUCommon.cpp
+++ b/GPU/GPUCommon.cpp
@@ -28,6 +28,7 @@
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/TextureCacheCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Record.h"
const CommonCommandTableEntry commonCommandTable[] = {
@@ -933,7 +934,7 @@ bool GPUCommon::InterpretList(DisplayList &list) {
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
debugRecording_ = GPURecord::IsActive();
- const bool useDebugger = host->GPUDebuggingActive() || debugRecording_;
+ const bool useDebugger = GPUDebug::IsActive() || debugRecording_;
const bool useFastRunLoop = !dumpThisFrame_ && !useDebugger;
while (gpuState == GPUSTATE_RUNNING) {
{
@@ -1035,7 +1036,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list)
const bool dumpThisFrame = dumpThisFrame_;
while (downcount > 0)
{
- host->GPUNotifyCommand(list.pc);
+ GPUDebug::NotifyCommand(list.pc);
GPURecord::NotifyCommand(list.pc);
u32 op = Memory::ReadUnchecked_U32(list.pc);
u32 cmd = op >> 24;
@@ -1586,10 +1587,8 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
if (!g_Config.bSoftwareSkinning)
vtypeCheckMask = 0xFFFFFFFF;
-#ifndef MOBILE_DEVICE
- if (debugRecording_ || host->GPUDebuggingActive())
+ if (debugRecording_ || GPUDebug::IsActive())
goto bail;
-#endif
while (src != stall) {
uint32_t data = *src;
diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp
index 381a40403a..2673ef4ab2 100644
--- a/GPU/Software/SoftGpu.cpp
+++ b/GPU/Software/SoftGpu.cpp
@@ -23,7 +23,6 @@
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/Debugger/Breakpoints.h"
-#include "Core/Host.h"
#include "Core/MemMap.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceGe.h"
@@ -39,6 +38,7 @@
#include "GPU/Software/TransformUnit.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/FramebufferCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Record.h"
const int FB_WIDTH = 480;
@@ -141,7 +141,7 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for
displayFramebuf_ = (framebuf & 0xFF000000) == 0 ? 0x44000000 | framebuf : framebuf;
displayStride_ = stride;
displayFormat_ = format;
- host->GPUNotifyDisplay(framebuf, stride, format);
+ GPUDebug::NotifyDisplay(framebuf, stride, format);
}
// Copies RGBA8 data from RAM to the currently bound render target.
diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp
index 05f1853119..1d9eef5a13 100644
--- a/GPU/Software/TransformUnit.cpp
+++ b/GPU/Software/TransformUnit.cpp
@@ -16,13 +16,12 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/MemoryUtil.h"
-#include "Core/Host.h"
#include "Core/Config.h"
#include "GPU/GPUState.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SplineCommon.h"
-
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Software/TransformUnit.h"
#include "GPU/Software/Clipper.h"
#include "GPU/Software/Lighting.h"
@@ -490,7 +489,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
break;
}
- host->GPUNotifyDraw();
+ GPUDebug::NotifyDraw();
}
// TODO: This probably is not the best interface.
diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp
index 0da871ba3e..16f0c495fb 100644
--- a/GPU/Vulkan/DrawEngineVulkan.cpp
+++ b/GPU/Vulkan/DrawEngineVulkan.cpp
@@ -25,7 +25,6 @@
#include "Common/MemoryUtil.h"
#include "Core/MemMap.h"
-#include "Core/Host.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
@@ -44,6 +43,7 @@
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Vulkan/DrawEngineVulkan.h"
#include "GPU/Vulkan/TextureCacheVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
@@ -976,7 +976,7 @@ void DrawEngineVulkan::DoFlush() {
gstate_c.vertBounds.maxU = 0;
gstate_c.vertBounds.maxV = 0;
- host->GPUNotifyDraw();
+ GPUDebug::NotifyDraw();
}
void DrawEngineVulkan::UpdateUBOs(FrameData *frame) {
diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp
index 69cd57feb6..18aaa60b2e 100644
--- a/GPU/Vulkan/GPU_Vulkan.cpp
+++ b/GPU/Vulkan/GPU_Vulkan.cpp
@@ -26,7 +26,6 @@
#include "Core/Config.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/MemMapHelpers.h"
-#include "Core/Host.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/System.h"
@@ -36,7 +35,7 @@
#include "GPU/ge_constants.h"
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
-
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Vulkan/GPU_Vulkan.h"
#include "GPU/Vulkan/FramebufferVulkan.h"
@@ -405,7 +404,7 @@ void GPU_Vulkan::UpdateVsyncInterval(bool force) {
}
void GPU_Vulkan::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
- host->GPUNotifyDisplay(framebuf, stride, format);
+ GPUDebug::NotifyDisplay(framebuf, stride, format);
framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format);
}
diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp
index f93783317c..14da475a95 100644
--- a/UI/GameSettingsScreen.cpp
+++ b/UI/GameSettingsScreen.cpp
@@ -1268,6 +1268,12 @@ void DeveloperToolsScreen::CreateViews() {
cpuTests->SetEnabled(TestsAvailable());
#endif
+ allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
+ canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
+ CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
+ list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
+ allowDebugger->SetEnabledPtr(&canAllowDebugger_);
+
list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig);
@@ -1284,12 +1290,6 @@ void DeveloperToolsScreen::CreateViews() {
createTextureIni->SetEnabled(false);
}
#endif
-
- allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
- canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
- CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
- list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
- allowDebugger->SetEnabledPtr(&canAllowDebugger_);
}
void DeveloperToolsScreen::onFinish(DialogResult result) {
diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj
index 0d1bc445ea..f3cfa762bf 100644
--- a/UWP/GPU_UWP/GPU_UWP.vcxproj
+++ b/UWP/GPU_UWP/GPU_UWP.vcxproj
@@ -332,6 +332,7 @@
+
@@ -390,6 +391,7 @@
+
diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters
index 641d4feef0..6108deb70b 100644
--- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters
+++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters
@@ -150,13 +150,18 @@
Debugger
+
+ Debugger
+
Debugger
Common
-
+
+ Software
+
Debugger
@@ -291,10 +296,15 @@
Debugger
+
+ Debugger
+
Debugger
-
+
+ Software
+
Debugger
diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp
index ad68557f64..0b878c7dab 100644
--- a/Windows/GEDebugger/GEDebugger.cpp
+++ b/Windows/GEDebugger/GEDebugger.cpp
@@ -31,7 +31,6 @@
#include "Windows/GEDebugger/TabVertices.h"
#include "Windows/W32Util/ShellUtil.h"
#include "Windows/InputBox.h"
-#include "Windows/WindowsHost.h"
#include "Windows/MainWindow.h"
#include "Windows/main.h"
#include "GPU/GPUInterface.h"
@@ -39,6 +38,7 @@
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GPUState.h"
#include "GPU/Debugger/Breakpoints.h"
+#include "GPU/Debugger/Debugger.h"
#include "GPU/Debugger/Record.h"
#include "GPU/Debugger/Stepping.h"
#include
@@ -47,12 +47,9 @@
const int POPUP_SUBMENU_ID_GEDBG_PREVIEW = 10;
using namespace GPUBreakpoints;
+using namespace GPUDebug;
using namespace GPUStepping;
-static bool attached = false;
-
-static BreakNextType breakNext = BREAK_NONE;
-
enum PrimaryDisplayType {
PRIMARY_FRAMEBUF,
PRIMARY_DEPTHBUF,
@@ -66,9 +63,6 @@ void CGEDebugger::Init() {
CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
: Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) {
- GPUBreakpoints::Init();
- Core_ListenStopRequest(ForceUnpause);
-
// minimum size = a little more than the default
RECT windowRect;
GetWindowRect(m_hDlg, &windowRect);
@@ -129,6 +123,8 @@ CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
int h = g_Config.iGEWindowH == -1 ? minHeight_ : g_Config.iGEWindowH;
MoveWindow(m_hDlg,x,y,w,h,FALSE);
+ SetTimer(m_hDlg, 1, USER_TIMER_MINIMUM, nullptr);
+
UpdateTextureLevel(textureLevel_);
}
@@ -318,6 +314,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) {
bool bufferResult = false;
u32 flags = SimpleGLWindow::ALPHA_IGNORE | SimpleGLWindow::RESIZE_SHRINK_CENTER;
+ SetupPreviews();
+
primaryBuffer_ = nullptr;
if (showClut_) {
bufferResult = GPU_GetCurrentTexture(primaryBuffer_, textureLevel_);
@@ -362,6 +360,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) {
void CGEDebugger::UpdateSecondPreview(const GPUgstate &state) {
bool bufferResult = false;
+ SetupPreviews();
+
secondBuffer_ = nullptr;
if (showClut_) {
bufferResult = GPU_GetCurrentClut(secondBuffer_);
@@ -404,6 +404,8 @@ void CGEDebugger::PrimaryPreviewHover(int x, int y) {
return;
}
+ SetupPreviews();
+
wchar_t desc[256] = {0};
if (!primaryWindow->HasTex()) {
@@ -626,14 +628,6 @@ void CGEDebugger::SavePosition()
}
}
-void CGEDebugger::SetBreakNext(BreakNextType type) {
- attached = true;
- SetupPreviews();
-
- breakNext = type;
- ResumeFromStepping();
-}
-
BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
@@ -657,9 +651,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
return TRUE;
case WM_CLOSE:
- attached = false;
- ResumeFromStepping();
- breakNext = BREAK_NONE;
+ GPUDebug::SetActive(false);
Show(false);
return TRUE;
@@ -674,6 +666,16 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
}
break;
+ case WM_TIMER:
+ if (GPUStepping::IsStepping()) {
+ static int lastCounter = 0;
+ if (lastCounter != GPUStepping::GetSteppingCounter()) {
+ UpdatePreviews();
+ lastCounter = GPUStepping::GetSteppingCounter();
+ }
+ }
+ break;
+
case WM_NOTIFY:
switch (wParam)
{
@@ -685,7 +687,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case IDC_GEDBG_FBTABS:
fbTabs->HandleNotify(lParam);
- if (attached && gpuDebug != nullptr) {
+ if (GPUDebug::IsActive() && gpuDebug != nullptr) {
UpdatePreviews();
}
break;
@@ -695,32 +697,28 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_GEDBG_STEPDRAW:
- SetBreakNext(BREAK_NEXT_DRAW);
+ SetBreakNext(BreakNext::DRAW);
break;
case IDC_GEDBG_STEP:
- SetBreakNext(BREAK_NEXT_OP);
+ SetBreakNext(BreakNext::OP);
break;
case IDC_GEDBG_STEPTEX:
- AddTextureChangeTempBreakpoint();
- SetBreakNext(BREAK_NEXT_TEX);
+ SetBreakNext(BreakNext::TEX);
break;
case IDC_GEDBG_STEPFRAME:
- SetBreakNext(BREAK_NEXT_FRAME);
+ SetBreakNext(BreakNext::FRAME);
break;
case IDC_GEDBG_STEPPRIM:
- AddCmdBreakpoint(GE_CMD_PRIM, true);
- AddCmdBreakpoint(GE_CMD_BEZIER, true);
- AddCmdBreakpoint(GE_CMD_SPLINE, true);
- SetBreakNext(BREAK_NEXT_PRIM);
+ SetBreakNext(BreakNext::PRIM);
break;
case IDC_GEDBG_BREAKTEX:
{
- attached = true;
+ GPUDebug::SetActive(true);
if (!gpuDebug) {
break;
}
@@ -739,7 +737,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case IDC_GEDBG_BREAKTARGET:
{
- attached = true;
+ GPUDebug::SetActive(true);
if (!gpuDebug) {
break;
}
@@ -758,26 +756,26 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case IDC_GEDBG_TEXLEVELDOWN:
UpdateTextureLevel(textureLevel_ - 1);
- if (attached && gpuDebug != nullptr) {
+ if (GPUDebug::IsActive() && gpuDebug != nullptr) {
UpdatePreviews();
}
break;
case IDC_GEDBG_TEXLEVELUP:
UpdateTextureLevel(textureLevel_ + 1);
- if (attached && gpuDebug != nullptr) {
+ if (GPUDebug::IsActive() && gpuDebug != nullptr) {
UpdatePreviews();
}
break;
case IDC_GEDBG_RESUME:
+ SetupPreviews();
primaryWindow->Clear();
secondWindow->Clear();
SetDlgItemText(m_hDlg, IDC_GEDBG_FRAMEBUFADDR, L"");
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L"");
- ResumeFromStepping();
- breakNext = BREAK_NONE;
+ SetBreakNext(BreakNext::NONE);
break;
case IDC_GEDBG_RECORD:
@@ -785,14 +783,14 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case IDC_GEDBG_FORCEOPAQUE:
- if (attached && gpuDebug != nullptr) {
+ if (GPUDebug::IsActive() && gpuDebug != nullptr) {
forceOpaque_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_FORCEOPAQUE), BM_GETCHECK, 0, 0) != 0;
UpdatePreviews();
}
break;
case IDC_GEDBG_SHOWCLUT:
- if (attached && gpuDebug != nullptr) {
+ if (GPUDebug::IsActive() && gpuDebug != nullptr) {
showClut_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_SHOWCLUT), BM_GETCHECK, 0, 0) != 0;
UpdatePreviews();
}
@@ -800,30 +798,13 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
}
break;
- case WM_GEDBG_BREAK_CMD:
- {
- u32 pc = (u32)wParam;
- ClearTempBreakpoints();
- auto info = gpuDebug->DissassembleOp(pc);
- NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str());
- UpdatePreviews();
- }
- break;
-
- case WM_GEDBG_BREAK_DRAW:
- {
- NOTICE_LOG(G3D, "Waiting at a draw");
- UpdatePreviews();
- }
- break;
-
case WM_GEDBG_STEPDISPLAYLIST:
- SetBreakNext(BREAK_NEXT_OP);
+ SetBreakNext(BreakNext::OP);
break;
case WM_GEDBG_TOGGLEPCBREAKPOINT:
{
- attached = true;
+ GPUDebug::SetActive(true);
u32 pc = (u32)wParam;
bool temp;
bool isBreak = IsAddressBreakpoint(pc, temp);
@@ -837,7 +818,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_GEDBG_RUNTOWPARAM:
{
- attached = true;
+ GPUDebug::SetActive(true);
u32 pc = (u32)wParam;
AddAddressBreakpoint(pc, true);
SendMessage(m_hDlg,WM_COMMAND,IDC_GEDBG_RESUME,0);
@@ -845,9 +826,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case WM_GEDBG_SETCMDWPARAM:
- {
- GPU_SetCmdValue((u32)wParam);
- }
+ GPU_SetCmdValue((u32)wParam);
break;
case WM_GEDBG_UPDATE_WATCH:
@@ -859,44 +838,3 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
return FALSE;
}
-
-// The below WindowsHost methods are called on the GPU thread.
-
-bool WindowsHost::GPUDebuggingActive() {
- return attached;
-}
-
-static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
- PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam);
-}
-
-static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) {
- if (attached) {
- EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam));
- }
-}
-
-void WindowsHost::GPUNotifyCommand(u32 pc) {
- u32 op = Memory::ReadUnchecked_U32(pc);
- u8 cmd = op >> 24;
-
- if (breakNext == BREAK_NEXT_OP || IsBreakpoint(pc, op)) {
- PauseWithMessage(WM_GEDBG_BREAK_CMD, (WPARAM) pc);
- }
-}
-
-void WindowsHost::GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {
- if (breakNext == BREAK_NEXT_FRAME) {
- // This should work fine, start stepping at the first op of the new frame.
- breakNext = BREAK_NEXT_OP;
- }
-}
-
-void WindowsHost::GPUNotifyDraw() {
- if (breakNext == BREAK_NEXT_DRAW) {
- PauseWithMessage(WM_GEDBG_BREAK_DRAW);
- }
-}
-
-void WindowsHost::GPUNotifyTextureAttachment(u32 addr) {
-}
diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h
index f47afc4236..79db21dc1b 100644
--- a/Windows/GEDebugger/GEDebugger.h
+++ b/Windows/GEDebugger/GEDebugger.h
@@ -19,31 +19,20 @@
#include "Common/CommonWindows.h"
#include "GPU/Common/GPUDebugInterface.h"
+#include "GPU/Debugger/Debugger.h"
#include "Windows/resource.h"
#include "Windows/W32Util/DialogManager.h"
#include "Windows/W32Util/TabControl.h"
#include "Windows/GEDebugger/SimpleGLWindow.h"
enum {
- WM_GEDBG_BREAK_CMD = WM_USER + 200,
- WM_GEDBG_BREAK_DRAW,
- WM_GEDBG_STEPDISPLAYLIST,
+ WM_GEDBG_STEPDISPLAYLIST = WM_USER + 200,
WM_GEDBG_TOGGLEPCBREAKPOINT,
WM_GEDBG_RUNTOWPARAM,
WM_GEDBG_SETCMDWPARAM,
WM_GEDBG_UPDATE_WATCH,
};
-enum BreakNextType {
- BREAK_NONE,
- BREAK_NEXT_OP,
- BREAK_NEXT_DRAW,
- BREAK_NEXT_TEX,
- BREAK_NEXT_NONTEX,
- BREAK_NEXT_FRAME,
- BREAK_NEXT_PRIM,
-};
-
class CtrlDisplayListView;
class TabDisplayLists;
class TabStateFlags;
@@ -76,7 +65,6 @@ private:
void HandleRedraw(int which);
void UpdateSize(WORD width, WORD height);
void SavePosition();
- void SetBreakNext(BreakNextType type);
void UpdateTextureLevel(int level);
void DescribePrimaryPreview(const GPUgstate &state, wchar_t desc[256]);
void DescribeSecondPreview(const GPUgstate &state, wchar_t desc[256]);
diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h
index f05aeca316..ed3ffeefbd 100644
--- a/Windows/WindowsHost.h
+++ b/Windows/WindowsHost.h
@@ -55,11 +55,6 @@ public:
void SaveSymbolMap() override;
void SetWindowTitle(const char *message) override;
- bool GPUDebuggingActive() override;
- void GPUNotifyCommand(u32 pc) override;
- void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override;
- void GPUNotifyDraw() override;
- void GPUNotifyTextureAttachment(u32 addr) override;
void ToggleDebugConsoleVisibility() override;
bool CanCreateShortcut() override;
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 171ca31310..1ed3b4eb69 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -242,6 +242,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/GPU/Common/PostShader.cpp \
$(SRC)/GPU/Common/ShaderUniforms.cpp \
$(SRC)/GPU/Debugger/Breakpoints.cpp \
+ $(SRC)/GPU/Debugger/Debugger.cpp \
$(SRC)/GPU/Debugger/Record.cpp \
$(SRC)/GPU/Debugger/Stepping.cpp \
$(SRC)/GPU/GLES/FramebufferManagerGLES.cpp \
@@ -309,6 +310,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \
+ $(SRC)/Core/Debugger/WebSocket/GPURecordSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \
diff --git a/libretro/Makefile.common b/libretro/Makefile.common
index fd6e262e35..13475121f5 100644
--- a/libretro/Makefile.common
+++ b/libretro/Makefile.common
@@ -168,8 +168,9 @@ SOURCES_CXX += \
$(GPUCOMMONDIR)/PostShader.cpp \
$(COMMONDIR)/ColorConv.cpp \
$(GPUDIR)/Debugger/Breakpoints.cpp \
- $(GPUDIR)/Debugger/Stepping.cpp \
+ $(GPUDIR)/Debugger/Debugger.cpp \
$(GPUDIR)/Debugger/Record.cpp \
+ $(GPUDIR)/Debugger/Stepping.cpp \
$(GPUDIR)/Common/TextureCacheCommon.cpp \
$(GPUDIR)/Common/TextureScalerCommon.cpp \
$(GPUDIR)/Common/SoftwareTransformCommon.cpp \