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 \