diff --git a/Core/Core.cpp b/Core/Core.cpp index d843047808dc..15d1c42d3b62 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -187,7 +187,7 @@ void Core_RunLoopUntil(u64 globalticks) { case CORE_RUNNING_GE: switch (gpu->ProcessDLQueue()) { case DLResult::DebugBreak: - GPUStepping::EnterStepping(); + GPUStepping::EnterStepping(coreState); break; case DLResult::Error: // We should elegantly report the error somehow, or I guess ignore it. diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp index 30516ba81e83..d8a54d2e3060 100644 --- a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp @@ -21,6 +21,8 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/System.h" #include "GPU/Debugger/Record.h" +#include "GPU/GPU.h" +#include "GPU/Common/GPUDebugInterface.h" struct WebSocketGPURecordState : public DebuggerSubscriber { ~WebSocketGPURecordState(); @@ -44,7 +46,7 @@ DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) { WebSocketGPURecordState::~WebSocketGPURecordState() { // Clear the callback to hopefully avoid a crash. if (pending_) - GPURecord::ClearCallback(); + gpuDebug->GetRecorder()->ClearCallback(); } // Begin recording (gpu.record.dump) @@ -60,7 +62,7 @@ void WebSocketGPURecordState::Dump(DebuggerRequest &req) { return req.Fail("CPU not started"); } - bool result = GPURecord::RecordNextFrame([=](const Path &filename) { + bool result = gpuDebug->GetRecorder()->RecordNextFrame([=](const Path &filename) { lastFilename_ = filename; pending_ = false; }); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 609f17dd00b2..a0ba14ca133f 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -676,7 +676,7 @@ void __DisplayFlip(int cyclesLate) { // 4 here means 1 drawn, 4 skipped - so 12 fps minimum. maxFrameskip = frameSkipNum; } - if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) { + if (numSkippedFrames >= maxFrameskip || gpuDebug->GetRecorder()->IsActivePending()) { skipFrame = false; } diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index cacc866f8f9a..84f034f9ee3b 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -140,14 +140,14 @@ bool FramebufferManagerCommon::PresentedThisFrame() const { return presentation_->PresentedThisFrame(); } -void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { +void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format, GPURecord::Recorder *recorder) { displayFramebufPtr_ = framebuf & 0x3FFFFFFF; if (Memory::IsVRAMAddress(displayFramebufPtr_)) displayFramebufPtr_ = framebuf & 0x041FFFFF; displayStride_ = stride; displayFormat_ = format; GPUDebug::NotifyDisplay(framebuf, stride, format); - GPURecord::NotifyDisplay(framebuf, stride, format); + recorder->NotifyDisplay(framebuf, stride, format); } VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const { diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 37a71d751b24..cbd13773dd01 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -273,6 +273,10 @@ namespace Draw { class DrawContext; } +namespace GPURecord { +class Recorder; +} + struct DrawPixelsEntry { Draw::Texture *tex; uint64_t contentsHash; @@ -302,7 +306,7 @@ class FramebufferManagerCommon { void Init(int msaaLevel); virtual void BeginFrame(); - void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); + void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format, GPURecord::Recorder *recorder); void DestroyFramebuf(VirtualFramebuffer *v); VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason); diff --git a/GPU/Common/GPUDebugInterface.h b/GPU/Common/GPUDebugInterface.h index 35e607db75a6..079798d5ca4c 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -34,6 +34,11 @@ class TextureCacheCommon; struct VirtualFramebuffer; struct DisplayList; +namespace GPURecord { +class Recorder; +} +class GPUBreakpoints; + struct GPUDebugOp { u32 pc; u8 cmd; @@ -239,6 +244,9 @@ class GPUDebugInterface { virtual const std::list &GetDisplayListQueue() = 0; virtual const DisplayList &GetDisplayList(int index) = 0; + virtual GPURecord::Recorder *GetRecorder() = 0; + virtual GPUBreakpoints *GetBreakpoints() = 0; + virtual bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { return false; } diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 84cbf9067954..5a8f3bd12d91 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1310,7 +1310,7 @@ void TextureCacheCommon::NotifyWriteFormattedFromMemory(u32 addr, int size, int videos_.push_back({ addr, (u32)size, gpuStats.numFlips }); } -void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { +void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes, GPURecord::Recorder *recorder) { if (loadBytes == 0) { // Don't accidentally overwrite clutTotalBytes_ with a zero. return; @@ -1430,7 +1430,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { u32 bytes = Memory::ValidSize(clutAddr, loadBytes); _assert_(bytes <= 2048); bool performDownload = PSP_CoreParameter().compat.flags().AllowDownloadCLUT; - if (GPURecord::IsActive()) + if (recorder->IsActive()) performDownload = true; if (clutRenderAddress_ != 0xFFFFFFFF && performDownload) { framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 48b29dd35990..48fc25dd54ef 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -63,6 +63,10 @@ class DrawContext; class Texture; } +namespace GPURecord { +class Recorder; +} + // Used by D3D11 and Vulkan, could be used by modern GL struct SamplerCacheKey { union { @@ -334,7 +338,7 @@ class TextureCacheCommon { TextureCacheCommon(Draw::DrawContext *draw, Draw2D *draw2D); virtual ~TextureCacheCommon(); - void LoadClut(u32 clutAddr, u32 loadBytes); + void LoadClut(u32 clutAddr, u32 loadBytes, GPURecord::Recorder *recorder); bool GetCurrentClutBuffer(GPUDebugBuffer &buffer); // This updates nextTexture_ / nextFramebufferTexture_, which is then used by ApplyTexture. diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index f941e4d17074..e1cfacdc90a8 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -485,7 +485,7 @@ void DrawEngineD3D11::DoFlush() { ResetAfterDrawInline(); framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); - GPUDebug::NotifyDraw(); + GPUDebug::NotifyFlush(); } TessellationDataTransferD3D11::TessellationDataTransferD3D11(ID3D11DeviceContext *context, ID3D11Device *device) diff --git a/GPU/Debugger/Breakpoints.cpp b/GPU/Debugger/Breakpoints.cpp index e4e521404b1b..5dc20f3f362b 100644 --- a/GPU/Debugger/Breakpoints.cpp +++ b/GPU/Debugger/Breakpoints.cpp @@ -22,43 +22,10 @@ #include #include "Common/CommonFuncs.h" -#include "Common/Math/expression_parser.h" -#include "GPU/Common/GPUDebugInterface.h" #include "GPU/Debugger/Breakpoints.h" +#include "GPU/Debugger/GECommandTable.h" #include "GPU/GPUState.h" -namespace GPUBreakpoints { - -static void NothingToDo(bool) { -} - -struct BreakpointInfo { - bool isConditional = false; - PostfixExpression expression; - std::string expressionString; -}; - -static std::mutex breaksLock; -static bool breakCmds[256]; -static BreakpointInfo breakCmdsInfo[256]; -static std::unordered_map breakPCs; -static std::set breakTextures; -static std::set breakRenderTargets; -// Small optimization to avoid a lock/lookup for the common case. -static size_t breakPCsCount = 0; -static size_t breakTexturesCount = 0; -static size_t breakRenderTargetsCount = 0; -static std::function notifyBreakpoints = &NothingToDo; - -// If these are set, the above are also, but they should be temporary. -static bool breakCmdsTemp[256]; -static std::set breakPCsTemp; -static std::set breakTexturesTemp; -static std::set breakRenderTargetsTemp; -static bool textureChangeTemp = false; - -static u32 lastTexture = 0xFFFFFFFF; - // These are commands we run before breaking on a texture. // They are commands that affect the decoding of the texture. const static u8 textureRelatedCmds[] = { @@ -74,10 +41,8 @@ const static u8 textureRelatedCmds[] = { // Sometimes found between clut/texture params. GE_CMD_TEXFLUSH, GE_CMD_TEXSYNC, }; -static std::vector nonTextureCmds; -void Init(void (*hasBreakpoints)(bool flag)) { - notifyBreakpoints = hasBreakpoints; +void GPUBreakpoints::Init() { ClearAllBreakpoints(); nonTextureCmds.clear(); @@ -87,7 +52,7 @@ void Init(void (*hasBreakpoints)(bool flag)) { } } -void AddNonTextureTempBreakpoints() { +void GPUBreakpoints::AddNonTextureTempBreakpoints() { for (int i = 0; i < 256; ++i) { if (nonTextureCmds[i]) { AddCmdBreakpoint(i, true); @@ -95,7 +60,7 @@ void AddNonTextureTempBreakpoints() { } } -u32 GetAdjustedTextureAddress(u32 op) { +static u32 GetAdjustedTextureAddress(u32 op) { const u8 cmd = op >> 24; bool interesting = (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7); interesting = interesting || (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7); @@ -117,7 +82,7 @@ u32 GetAdjustedTextureAddress(u32 op) { return addr; } -u32 GetAdjustedRenderTargetAddress(u32 op) { +static u32 GetAdjustedRenderTargetAddress(u32 op) { const u8 cmd = op >> 24; switch (cmd) { case GE_CMD_FRAMEBUFPTR: @@ -129,7 +94,7 @@ u32 GetAdjustedRenderTargetAddress(u32 op) { } // Note: this now always returns false, but still needs to be called. -void CheckForTextureChange(u32 op, u32 addr) { +void GPUBreakpoints::CheckForTextureChange(u32 op, u32 addr) { if (!textureChangeTemp) { return; } @@ -159,7 +124,7 @@ void CheckForTextureChange(u32 op, u32 addr) { } } -bool IsTextureCmdBreakpoint(u32 op) { +bool GPUBreakpoints::IsTextureCmdBreakpoint(u32 op) { const u32 addr = GetAdjustedTextureAddress(op); if (addr != (u32)-1) { CheckForTextureChange(op, addr); @@ -170,7 +135,7 @@ bool IsTextureCmdBreakpoint(u32 op) { } } -bool IsRenderTargetCmdBreakpoint(u32 op) { +bool GPUBreakpoints::IsRenderTargetCmdBreakpoint(u32 op) { const u32 addr = GetAdjustedRenderTargetAddress(op); if (addr != (u32)-1) { return IsRenderTargetBreakpoint(addr); @@ -178,7 +143,7 @@ bool IsRenderTargetCmdBreakpoint(u32 op) { return false; } -static bool HitBreakpointCond(BreakpointInfo &bp, u32 op) { +static bool HitBreakpointCond(GPUBreakpoints::BreakpointInfo &bp, u32 op) { u8 cmd = op >> 24; // Temporarily set the value while running the breakpoint. @@ -195,7 +160,7 @@ static bool HitBreakpointCond(BreakpointInfo &bp, u32 op) { return result != 0; } -static bool HitAddressBreakpoint(u32 pc, u32 op) { +bool GPUBreakpoints::HitAddressBreakpoint(u32 pc, u32 op) { if (breakPCsCount == 0) return false; @@ -210,7 +175,7 @@ static bool HitAddressBreakpoint(u32 pc, u32 op) { return true; } -static bool HitOpBreakpoint(u32 op) { +bool GPUBreakpoints::HitOpBreakpoint(u32 op) { u8 cmd = op >> 24; if (!IsCmdBreakpoint(cmd)) return false; @@ -223,7 +188,7 @@ static bool HitOpBreakpoint(u32 op) { return true; } -bool IsBreakpoint(u32 pc, u32 op) { +bool GPUBreakpoints::IsBreakpoint(u32 pc, u32 op) { if (HitAddressBreakpoint(pc, op) || HitOpBreakpoint(op)) { return true; } @@ -239,7 +204,7 @@ bool IsBreakpoint(u32 pc, u32 op) { return false; } -bool IsAddressBreakpoint(u32 addr, bool &temp) { +bool GPUBreakpoints::IsAddressBreakpoint(u32 addr, bool &temp) { if (breakPCsCount == 0) { temp = false; return false; @@ -250,7 +215,7 @@ bool IsAddressBreakpoint(u32 addr, bool &temp) { return breakPCs.find(addr) != breakPCs.end(); } -bool IsAddressBreakpoint(u32 addr) { +bool GPUBreakpoints::IsAddressBreakpoint(u32 addr) { if (breakPCsCount == 0) { return false; } @@ -259,7 +224,7 @@ bool IsAddressBreakpoint(u32 addr) { return breakPCs.find(addr) != breakPCs.end(); } -bool IsTextureBreakpoint(u32 addr, bool &temp) { +bool GPUBreakpoints::IsTextureBreakpoint(u32 addr, bool &temp) { if (breakTexturesCount == 0) { temp = false; return false; @@ -270,7 +235,7 @@ bool IsTextureBreakpoint(u32 addr, bool &temp) { return breakTextures.find(addr) != breakTextures.end(); } -bool IsTextureBreakpoint(u32 addr) { +bool GPUBreakpoints::IsTextureBreakpoint(u32 addr) { if (breakTexturesCount == 0) { return false; } @@ -279,7 +244,7 @@ bool IsTextureBreakpoint(u32 addr) { return breakTextures.find(addr) != breakTextures.end(); } -bool IsRenderTargetBreakpoint(u32 addr, bool &temp) { +bool GPUBreakpoints::IsRenderTargetBreakpoint(u32 addr, bool &temp) { if (breakRenderTargetsCount == 0) { temp = false; return false; @@ -292,7 +257,7 @@ bool IsRenderTargetBreakpoint(u32 addr, bool &temp) { return breakRenderTargets.find(addr) != breakRenderTargets.end(); } -bool IsRenderTargetBreakpoint(u32 addr) { +bool GPUBreakpoints::IsRenderTargetBreakpoint(u32 addr) { if (breakRenderTargetsCount == 0) { return false; } @@ -303,24 +268,24 @@ bool IsRenderTargetBreakpoint(u32 addr) { return breakRenderTargets.find(addr) != breakRenderTargets.end(); } -bool IsOpBreakpoint(u32 op, bool &temp) { +bool GPUBreakpoints::IsOpBreakpoint(u32 op, bool &temp) { return IsCmdBreakpoint(op >> 24, temp); } -bool IsOpBreakpoint(u32 op) { +bool GPUBreakpoints::IsOpBreakpoint(u32 op) { return IsCmdBreakpoint(op >> 24); } -bool IsCmdBreakpoint(u8 cmd, bool &temp) { +bool GPUBreakpoints::IsCmdBreakpoint(u8 cmd, bool &temp) { temp = breakCmdsTemp[cmd]; return breakCmds[cmd]; } -bool IsCmdBreakpoint(u8 cmd) { +bool GPUBreakpoints::IsCmdBreakpoint(u8 cmd) { return breakCmds[cmd]; } -static bool HasAnyBreakpoints() { +bool GPUBreakpoints::HasAnyBreakpoints() const { if (breakPCsCount != 0 || breakTexturesCount != 0 || breakRenderTargetsCount != 0) return true; if (textureChangeTemp) @@ -334,7 +299,7 @@ static bool HasAnyBreakpoints() { return false; } -void AddAddressBreakpoint(u32 addr, bool temp) { +void GPUBreakpoints::AddAddressBreakpoint(u32 addr, bool temp) { std::lock_guard guard(breaksLock); if (temp) { @@ -350,10 +315,10 @@ void AddAddressBreakpoint(u32 addr, bool temp) { } breakPCsCount = breakPCs.size(); - notifyBreakpoints(true); + hasBreakpoints_ = true; } -void AddCmdBreakpoint(u8 cmd, bool temp) { +void GPUBreakpoints::AddCmdBreakpoint(u8 cmd, bool temp) { if (temp) { if (!breakCmds[cmd]) { breakCmdsTemp[cmd] = true; @@ -369,10 +334,10 @@ void AddCmdBreakpoint(u8 cmd, bool temp) { breakCmdsInfo[cmd].isConditional = false; } } - notifyBreakpoints(true); + hasBreakpoints_ = true; } -void AddTextureBreakpoint(u32 addr, bool temp) { +void GPUBreakpoints::AddTextureBreakpoint(u32 addr, bool temp) { std::lock_guard guard(breaksLock); if (temp) { @@ -386,10 +351,10 @@ void AddTextureBreakpoint(u32 addr, bool temp) { } breakTexturesCount = breakTextures.size(); - notifyBreakpoints(true); + hasBreakpoints_ = true; } -void AddRenderTargetBreakpoint(u32 addr, bool temp) { +void GPUBreakpoints::AddRenderTargetBreakpoint(u32 addr, bool temp) { std::lock_guard guard(breaksLock); addr &= 0x001FFFF0; @@ -405,50 +370,50 @@ void AddRenderTargetBreakpoint(u32 addr, bool temp) { } breakRenderTargetsCount = breakRenderTargets.size(); - notifyBreakpoints(true); + hasBreakpoints_ = true; } -void AddTextureChangeTempBreakpoint() { +void GPUBreakpoints::AddTextureChangeTempBreakpoint() { textureChangeTemp = true; - notifyBreakpoints(true); + hasBreakpoints_ = true; } -void AddAnyTempBreakpoint() { +void GPUBreakpoints::AddAnyTempBreakpoint() { for (int i = 0; i < 256; ++i) { AddCmdBreakpoint(i, true); } - notifyBreakpoints(true); + hasBreakpoints_ = true; } -void RemoveAddressBreakpoint(u32 addr) { +void GPUBreakpoints::RemoveAddressBreakpoint(u32 addr) { std::lock_guard guard(breaksLock); breakPCsTemp.erase(addr); breakPCs.erase(addr); breakPCsCount = breakPCs.size(); - notifyBreakpoints(HasAnyBreakpoints()); + hasBreakpoints_ = HasAnyBreakpoints(); } -void RemoveCmdBreakpoint(u8 cmd) { +void GPUBreakpoints::RemoveCmdBreakpoint(u8 cmd) { std::lock_guard guard(breaksLock); breakCmdsTemp[cmd] = false; breakCmds[cmd] = false; - notifyBreakpoints(HasAnyBreakpoints()); + hasBreakpoints_ = HasAnyBreakpoints(); } -void RemoveTextureBreakpoint(u32 addr) { +void GPUBreakpoints::RemoveTextureBreakpoint(u32 addr) { std::lock_guard guard(breaksLock); breakTexturesTemp.erase(addr); breakTextures.erase(addr); breakTexturesCount = breakTextures.size(); - notifyBreakpoints(HasAnyBreakpoints()); + hasBreakpoints_ = HasAnyBreakpoints(); } -void RemoveRenderTargetBreakpoint(u32 addr) { +void GPUBreakpoints::RemoveRenderTargetBreakpoint(u32 addr) { std::lock_guard guard(breaksLock); addr &= 0x001FFFF0; @@ -457,17 +422,17 @@ void RemoveRenderTargetBreakpoint(u32 addr) { breakRenderTargets.erase(addr); breakRenderTargetsCount = breakRenderTargets.size(); - notifyBreakpoints(HasAnyBreakpoints()); + hasBreakpoints_ = HasAnyBreakpoints(); } -void RemoveTextureChangeTempBreakpoint() { +void GPUBreakpoints::RemoveTextureChangeTempBreakpoint() { std::lock_guard guard(breaksLock); textureChangeTemp = false; - notifyBreakpoints(HasAnyBreakpoints()); + hasBreakpoints_ = HasAnyBreakpoints(); } -static bool SetupCond(BreakpointInfo &bp, const std::string &expression, std::string *error) { +static bool SetupCond(GPUBreakpoints::BreakpointInfo &bp, const std::string &expression, std::string *error) { bool success = true; if (expression.length() != 0) { if (GPUDebugInitExpression(gpuDebug, expression.c_str(), bp.expression)) { @@ -485,7 +450,7 @@ static bool SetupCond(BreakpointInfo &bp, const std::string &expression, std::st return success; } -bool SetAddressBreakpointCond(u32 addr, const std::string &expression, std::string *error) { +bool GPUBreakpoints::SetAddressBreakpointCond(u32 addr, const std::string &expression, std::string *error) { // Must have one in the first place, make sure it's not temporary. AddAddressBreakpoint(addr); @@ -494,7 +459,7 @@ bool SetAddressBreakpointCond(u32 addr, const std::string &expression, std::stri return SetupCond(breakPCs[addr], expression, error); } -bool GetAddressBreakpointCond(u32 addr, std::string *expression) { +bool GPUBreakpoints::GetAddressBreakpointCond(u32 addr, std::string *expression) { std::lock_guard guard(breaksLock); auto entry = breakPCs.find(addr); if (entry != breakPCs.end() && entry->second.isConditional) { @@ -505,7 +470,7 @@ bool GetAddressBreakpointCond(u32 addr, std::string *expression) { return false; } -bool SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error) { +bool GPUBreakpoints::SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error) { // Must have one in the first place, make sure it's not temporary. AddCmdBreakpoint(cmd); @@ -513,7 +478,7 @@ bool SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *er return SetupCond(breakCmdsInfo[cmd], expression, error); } -bool GetCmdBreakpointCond(u8 cmd, std::string *expression) { +bool GPUBreakpoints::GetCmdBreakpointCond(u8 cmd, std::string *expression) { if (breakCmds[cmd] && breakCmdsInfo[cmd].isConditional) { if (expression) { std::lock_guard guard(breaksLock); @@ -524,11 +489,11 @@ bool GetCmdBreakpointCond(u8 cmd, std::string *expression) { return false; } -void UpdateLastTexture(u32 addr) { +void GPUBreakpoints::UpdateLastTexture(u32 addr) { lastTexture = addr; } -void ClearAllBreakpoints() { +void GPUBreakpoints::ClearAllBreakpoints() { std::lock_guard guard(breaksLock); for (int i = 0; i < 256; ++i) { @@ -548,10 +513,10 @@ void ClearAllBreakpoints() { breakRenderTargetsCount = breakRenderTargets.size(); textureChangeTemp = false; - notifyBreakpoints(false); + hasBreakpoints_ = false; } -void ClearTempBreakpoints() { +void GPUBreakpoints::ClearTempBreakpoints() { std::lock_guard guard(breaksLock); // Reset ones that were temporary back to non-breakpoints in the primary arrays. @@ -581,7 +546,24 @@ void ClearTempBreakpoints() { breakRenderTargetsCount = breakRenderTargets.size(); textureChangeTemp = false; - notifyBreakpoints(HasAnyBreakpoints()); + hasBreakpoints_ = HasAnyBreakpoints(); +} + +bool GPUBreakpoints::ToggleCmdBreakpoint(const GECmdInfo &info) { + if (IsCmdBreakpoint(info.cmd)) { + RemoveCmdBreakpoint(info.cmd); + if (info.otherCmd) + RemoveCmdBreakpoint(info.otherCmd); + if (info.otherCmd2) + RemoveCmdBreakpoint(info.otherCmd2); + return false; + } + + AddCmdBreakpoint(info.cmd); + if (info.otherCmd) + AddCmdBreakpoint(info.otherCmd); + if (info.otherCmd2) + AddCmdBreakpoint(info.otherCmd2); + return true; } -}; diff --git a/GPU/Debugger/Breakpoints.h b/GPU/Debugger/Breakpoints.h index cdb3215abf86..10de2f005d35 100644 --- a/GPU/Debugger/Breakpoints.h +++ b/GPU/Debugger/Breakpoints.h @@ -18,10 +18,21 @@ #pragma once #include +#include +#include +#include #include "Common/CommonTypes.h" +#include "Common/Math/expression_parser.h" +#include "GPU/Common/GPUDebugInterface.h" -namespace GPUBreakpoints { - void Init(void (*hasBreakpoints)(bool flag)); +struct GECmdInfo; + +class GPUBreakpoints { +public: + GPUBreakpoints() { + Init(); + } + void Init(); bool IsBreakpoint(u32 pc, u32 op); @@ -61,4 +72,49 @@ namespace GPUBreakpoints { bool IsOpBreakpoint(u32 op, bool &temp); bool IsOpBreakpoint(u32 op); + + bool HasBreakpoints() const { + return hasBreakpoints_; + } + + struct BreakpointInfo { + bool isConditional = false; + PostfixExpression expression; + std::string expressionString; + }; + + bool ToggleCmdBreakpoint(const GECmdInfo &info); + +private: + void AddNonTextureTempBreakpoints(); + void CheckForTextureChange(u32 op, u32 addr); + bool HasAnyBreakpoints() const; + bool IsTextureCmdBreakpoint(u32 op); + bool IsRenderTargetCmdBreakpoint(u32 op); + bool HitAddressBreakpoint(u32 pc, u32 op); + bool HitOpBreakpoint(u32 op); + + std::mutex breaksLock; + + bool breakCmds[256]{}; + BreakpointInfo breakCmdsInfo[256]{}; + std::unordered_map breakPCs; + std::set breakTextures; + std::set breakRenderTargets; + // Small optimization to avoid a lock/lookup for the common case. + size_t breakPCsCount = 0; + size_t breakTexturesCount = 0; + size_t breakRenderTargetsCount = 0; + + // If these are set, the above are also, but they should be temporary. + bool breakCmdsTemp[256]; + std::set breakPCsTemp; + std::set breakTexturesTemp; + std::set breakRenderTargetsTemp; + bool textureChangeTemp = false; + + u32 lastTexture = 0xFFFFFFFF; + std::vector nonTextureCmds; + + bool hasBreakpoints_ = false; // cached value of HasAnyBreakpoints(). }; diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp index fdad58cda158..f2d8d616b365 100644 --- a/GPU/Debugger/Debugger.cpp +++ b/GPU/Debugger/Debugger.cpp @@ -26,11 +26,8 @@ namespace GPUDebug { -static bool active = false; -static bool inited = false; static BreakNext breakNext = BreakNext::NONE; static int breakAtCount = -1; -static bool hasBreakpoints = false; static int primsLastFrame = 0; static int primsThisFrame = 0; @@ -60,58 +57,34 @@ const char *BreakNextToString(BreakNext next) { } } -static void Init() { - if (!inited) { - GPUBreakpoints::Init([](bool flag) { - hasBreakpoints = flag; - }); - inited = true; - } +bool NeedsSlowInterpreter() { + return breakNext != BreakNext::NONE; } -void SetActive(bool flag) { - Init(); - - active = flag; - if (!active) { - breakNext = BreakNext::NONE; - breakAtCount = -1; - GPUStepping::ResumeFromStepping(); - lastStepTime = -1.0; - } - /* - active = false; +void ClearBreak() { + breakNext = BreakNext::NONE; breakAtCount = -1; - hasBreakpoints = false; - thisFlipNum = 0; - g_primAfterDraw = false; - lastStepTime = -1.0f; - g_skipPcOnce = false; - */ -} - -bool IsActive() { - return active; + GPUStepping::ResumeFromStepping(); + lastStepTime = -1.0; } BreakNext GetBreakNext() { return breakNext; } -void SetBreakNext(BreakNext next) { - SetActive(true); +void SetBreakNext(BreakNext next, GPUBreakpoints *breakpoints) { breakNext = next; breakAtCount = -1; if (next == BreakNext::TEX) { - GPUBreakpoints::AddTextureChangeTempBreakpoint(); + breakpoints->AddTextureChangeTempBreakpoint(); } else if (next == BreakNext::PRIM || next == BreakNext::COUNT) { - GPUBreakpoints::AddCmdBreakpoint(GE_CMD_PRIM, true); - GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true); - GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true); - GPUBreakpoints::AddCmdBreakpoint(GE_CMD_VAP, true); + breakpoints->AddCmdBreakpoint(GE_CMD_PRIM, true); + breakpoints->AddCmdBreakpoint(GE_CMD_BEZIER, true); + breakpoints->AddCmdBreakpoint(GE_CMD_SPLINE, true); + breakpoints->AddCmdBreakpoint(GE_CMD_VAP, true); } else if (next == BreakNext::CURVE) { - GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true); - GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true); + breakpoints->AddCmdBreakpoint(GE_CMD_BEZIER, true); + breakpoints->AddCmdBreakpoint(GE_CMD_SPLINE, true); } else if (next == BreakNext::DRAW) { // This is now handled by switching to BreakNext::PRIM when we encounter a flush. // This will take us to the following actual draw. @@ -132,12 +105,7 @@ void SetBreakCount(int c, bool relative) { } } -NotifyResult NotifyCommand(u32 pc) { - if (!active) { - _dbg_assert_(false); - return NotifyResult::Skip; // return false - } - +NotifyResult NotifyCommand(u32 pc, GPUBreakpoints *breakpoints) { u32 op = Memory::ReadUnchecked_U32(pc); u32 cmd = op >> 24; if (thisFlipNum != gpuStats.numFlips) { @@ -166,8 +134,8 @@ NotifyResult NotifyCommand(u32 pc) { isBreakpoint = true; } else if (breakNext == BreakNext::COUNT) { isBreakpoint = primsThisFrame == breakAtCount; - } else if (hasBreakpoints) { - isBreakpoint = GPUBreakpoints::IsBreakpoint(pc, op); + } else if (breakpoints->HasBreakpoints()) { + isBreakpoint = breakpoints->IsBreakpoint(pc, op); } if (isBreakpoint && pc == g_skipPcOnce) { @@ -178,7 +146,7 @@ NotifyResult NotifyCommand(u32 pc) { g_skipPcOnce = 0; if (isBreakpoint) { - GPUBreakpoints::ClearTempBreakpoints(); + breakpoints->ClearTempBreakpoints(); if (coreState == CORE_POWERDOWN || !gpuDebug) { breakNext = BreakNext::NONE; @@ -201,33 +169,26 @@ NotifyResult NotifyCommand(u32 pc) { return process ? NotifyResult::Execute : NotifyResult::Skip; } -// TODO: This mechanism is a bit hacky. -void NotifyDraw() { - if (!active) - return; +void NotifyFlush() { if (breakNext == BreakNext::DRAW && !GPUStepping::IsStepping()) { - // Hack to handle draw notifications, that don't come in via NotifyCommand. + // Break on the first PRIM after a flush. if (g_primAfterDraw) { NOTICE_LOG(Log::GeDebugger, "Flush detected, breaking at next PRIM"); g_primAfterDraw = false; // Switch to PRIM mode. - SetBreakNext(BreakNext::PRIM); + SetBreakNext(BreakNext::PRIM, gpuDebug->GetBreakpoints()); } } } 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. + // Start stepping at the first op of the new frame. breakNext = BreakNext::OP; } } void NotifyBeginFrame() { - if (!active) - return; if (breakNext == BreakNext::VSYNC) { // Just start stepping as soon as we can once the vblank finishes. breakNext = BreakNext::OP; @@ -252,7 +213,6 @@ static bool ParseRange(const std::string &s, std::pair &range) { } bool SetRestrictPrims(const char *rule) { - SetActive(true); if (rule == nullptr || rule[0] == 0 || (rule[0] == '*' && rule[1] == 0)) { restrictPrimRanges.clear(); restrictPrimRule.clear(); diff --git a/GPU/Debugger/Debugger.h b/GPU/Debugger/Debugger.h index d7cfb7a7bf9e..fa99d039d612 100644 --- a/GPU/Debugger/Debugger.h +++ b/GPU/Debugger/Debugger.h @@ -34,10 +34,9 @@ enum class BreakNext { COUNT, }; -void SetActive(bool flag); -bool IsActive(); +bool NeedsSlowInterpreter(); -void SetBreakNext(BreakNext next); +void SetBreakNext(BreakNext next, GPUBreakpoints *breakpoints); void SetBreakCount(int c, bool relative = false); BreakNext GetBreakNext(); const char *BreakNextToString(BreakNext next); @@ -49,8 +48,8 @@ enum class NotifyResult { }; // While debugging is active, these may block. -NotifyResult NotifyCommand(u32 pc); -void NotifyDraw(); +NotifyResult NotifyCommand(u32 pc, GPUBreakpoints *breakpoints); +void NotifyFlush(); void NotifyDisplay(u32 framebuf, u32 stride, int format); void NotifyBeginFrame(); diff --git a/GPU/Debugger/GECommandTable.cpp b/GPU/Debugger/GECommandTable.cpp index 73898fdc6ceb..97c34f181055 100644 --- a/GPU/Debugger/GECommandTable.cpp +++ b/GPU/Debugger/GECommandTable.cpp @@ -417,23 +417,3 @@ const GECmdInfo &GECmdInfoByCmd(GECommand reg) { _assert_msg_((reg & 0xFF) == reg, "Invalid reg"); return geCmdInfo[reg & 0xFF]; } - -bool ToggleBreakpoint(const GECmdInfo &info) { - using namespace GPUBreakpoints; - if (IsCmdBreakpoint(info.cmd)) { - RemoveCmdBreakpoint(info.cmd); - if (info.otherCmd) - RemoveCmdBreakpoint(info.otherCmd); - if (info.otherCmd2) - RemoveCmdBreakpoint(info.otherCmd2); - return false; - } - - AddCmdBreakpoint(info.cmd); - if (info.otherCmd) - AddCmdBreakpoint(info.otherCmd); - if (info.otherCmd2) - AddCmdBreakpoint(info.otherCmd2); - return true; -} - diff --git a/GPU/Debugger/GECommandTable.h b/GPU/Debugger/GECommandTable.h index cbe3091cffda..0cb58fb8e24e 100644 --- a/GPU/Debugger/GECommandTable.h +++ b/GPU/Debugger/GECommandTable.h @@ -130,5 +130,3 @@ struct GECmdInfo { bool GECmdInfoByName(const char *name, GECmdInfo &info); const GECmdInfo &GECmdInfoByCmd(GECommand reg); - -bool ToggleBreakpoint(const GECmdInfo &info); diff --git a/GPU/Debugger/Playback.cpp b/GPU/Debugger/Playback.cpp index 1a2b951bb1ab..e9122e796d3b 100644 --- a/GPU/Debugger/Playback.cpp +++ b/GPU/Debugger/Playback.cpp @@ -909,7 +909,7 @@ void WriteRunDumpCode(u32 codeStart) { // This is called by the syscall. ReplayResult RunMountedReplay(const std::string &filename) { - _assert_msg_(!GPURecord::IsActivePending(), "Cannot run replay while recording."); + _assert_msg_(!gpuDebug->GetRecorder()->IsActivePending(), "Cannot run replay while recording."); Core_ListenStopRequest(&ReplayStop); diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index f08b2067aa03..67d03452c687 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -48,35 +48,9 @@ namespace GPURecord { -static bool active = false; -static std::atomic nextFrame = false; -static int flipLastAction = -1; -static int flipFinishAt = -1; -static uint32_t lastEdramTrans = 0x400; -static std::function writeCallback; - -static std::vector pushbuf; -static std::vector commands; -static std::vector lastRegisters; -static std::vector lastTextures; -static std::set lastRenderTargets; -static std::vector lastVRAM; - -enum class DirtyVRAMFlag : uint8_t { - CLEAN = 0, - UNKNOWN = 1, - DIRTY = 2, - DRAWN = 3, -}; -static constexpr uint32_t DIRTY_VRAM_SHIFT = 8; -static constexpr uint32_t DIRTY_VRAM_ROUND = (1 << DIRTY_VRAM_SHIFT) - 1; -static constexpr uint32_t DIRTY_VRAM_SIZE = (2 * 1024 * 1024) >> DIRTY_VRAM_SHIFT; -static constexpr uint32_t DIRTY_VRAM_MASK = (2 * 1024 * 1024 - 1) >> DIRTY_VRAM_SHIFT; -static DirtyVRAMFlag dirtyVRAM[DIRTY_VRAM_SIZE]; - -static void FlushRegisters() { +void Recorder::FlushRegisters() { if (!lastRegisters.empty()) { - Command last{CommandType::REGISTERS}; + Command last{ CommandType::REGISTERS }; last.ptr = (u32)pushbuf.size(); last.sz = (u32)(lastRegisters.size() * sizeof(u32)); pushbuf.resize(pushbuf.size() + last.sz); @@ -107,7 +81,7 @@ static Path GenRecordingFilename() { return dumpDir / StringFromFormat("%s_%04d.ppdmp", prefix.c_str(), 9999); } -static void DirtyAllVRAM(DirtyVRAMFlag flag) { +void Recorder::DirtyAllVRAM(DirtyVRAMFlag flag) { if (flag == DirtyVRAMFlag::UNKNOWN) { for (uint32_t i = 0; i < DIRTY_VRAM_SIZE; ++i) { if (dirtyVRAM[i] == DirtyVRAMFlag::CLEAN) @@ -119,7 +93,7 @@ static void DirtyAllVRAM(DirtyVRAMFlag flag) { } } -static void DirtyVRAM(u32 start, u32 sz, DirtyVRAMFlag flag) { +void Recorder::DirtyVRAM(u32 start, u32 sz, DirtyVRAMFlag flag) { u32 count = (sz + DIRTY_VRAM_ROUND) >> DIRTY_VRAM_SHIFT; u32 first = (start >> DIRTY_VRAM_SHIFT) & DIRTY_VRAM_MASK; if (first + count > DIRTY_VRAM_SIZE) { @@ -131,7 +105,7 @@ static void DirtyVRAM(u32 start, u32 sz, DirtyVRAMFlag flag) { dirtyVRAM[first + i] = flag; } -static void DirtyDrawnVRAM() { +void Recorder::DirtyDrawnVRAM() { int w = std::min(gstate.getScissorX2(), gstate.getRegionX2()) + 1; int h = std::min(gstate.getScissorY2(), gstate.getRegionY2()) + 1; @@ -151,7 +125,7 @@ static void DirtyDrawnVRAM() { DirtyVRAM(gstate.getFrameBufAddress(), bytes, DirtyVRAMFlag::DRAWN); } -static bool BeginRecording() { +bool Recorder::BeginRecording() { if (PSP_CoreParameter().fileType == IdentifiedFileType::PPSSPP_GE_DUMP) { // Can't record a GE dump. return false; @@ -168,7 +142,7 @@ static bool BeginRecording() { u32 sz = 512 * 4; pushbuf.resize(pushbuf.size() + sz); gstate.Save((u32_le *)(pushbuf.data() + ptr)); - commands.push_back({CommandType::INIT, sz, ptr}); + commands.push_back({ CommandType::INIT, sz, ptr }); lastVRAM.resize(2 * 1024 * 1024); // Also save the initial CLUT. @@ -195,10 +169,10 @@ static void WriteCompressed(FILE *fp, const void *p, size_t sz) { fwrite(&write_size, sizeof(write_size), 1, fp); fwrite(compressed, compressed_size, 1, fp); - delete [] compressed; + delete[] compressed; } -static Path WriteRecording() { +Path Recorder::WriteRecording() { FlushRegisters(); const Path filename = GenRecordingFilename(); @@ -298,10 +272,10 @@ static const u8 *mymemmem(const u8 *haystack, size_t off, size_t hlen, const u8 return result; } -static Command EmitCommandWithRAM(CommandType t, const void *p, u32 sz, u32 align) { +Command Recorder::EmitCommandWithRAM(CommandType t, const void *p, u32 sz, u32 align) { FlushRegisters(); - Command cmd{t, sz, 0}; + Command cmd{ t, sz, 0 }; if (sz) { // If at all possible, try to find it already in the buffer. @@ -337,7 +311,7 @@ static Command EmitCommandWithRAM(CommandType t, const void *p, u32 sz, u32 alig return cmd; } -static void UpdateLastVRAM(u32 addr, u32 bytes) { +void Recorder::UpdateLastVRAM(u32 addr, u32 bytes) { u32 base = addr & 0x001FFFFF; if (base + bytes > 0x00200000) { memcpy(&lastVRAM[base], Memory::GetPointerUnchecked(0x04000000 | base), 0x00200000 - base); @@ -347,7 +321,7 @@ static void UpdateLastVRAM(u32 addr, u32 bytes) { memcpy(&lastVRAM[base], Memory::GetPointerUnchecked(0x04000000 | base), bytes); } -static void ClearLastVRAM(u32 addr, u8 c, u32 bytes) { +void Recorder::ClearLastVRAM(u32 addr, u8 c, u32 bytes) { u32 base = addr & 0x001FFFFF; if (base + bytes > 0x00200000) { memset(&lastVRAM[base], c, 0x00200000 - base); @@ -357,7 +331,7 @@ static void ClearLastVRAM(u32 addr, u8 c, u32 bytes) { memset(&lastVRAM[base], c, bytes); } -static int CompareLastVRAM(u32 addr, u32 bytes) { +int Recorder::CompareLastVRAM(u32 addr, u32 bytes) const { u32 base = addr & 0x001FFFFF; if (base + bytes > 0x00200000) { int result = memcmp(&lastVRAM[base], Memory::GetPointerUnchecked(0x04000000 | base), 0x00200000 - base); @@ -370,7 +344,7 @@ static int CompareLastVRAM(u32 addr, u32 bytes) { return memcmp(&lastVRAM[base], Memory::GetPointerUnchecked(0x04000000 | base), bytes); } -static u32 GetTargetFlags(u32 addr, u32 sizeInRAM) { +u32 Recorder::GetTargetFlags(u32 addr, u32 sizeInRAM) { addr &= 0x041FFFFF; const bool isTarget = lastRenderTargets.find(addr) != lastRenderTargets.end(); @@ -416,7 +390,7 @@ static u32 GetTargetFlags(u32 addr, u32 sizeInRAM) { return flags; } -static void EmitTextureData(int level, u32 texaddr) { +void Recorder::EmitTextureData(int level, u32 texaddr) { GETextureFormat format = gstate.getTextureFormat(); int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); @@ -462,7 +436,7 @@ static void EmitTextureData(int level, u32 texaddr) { } if (memcmp(pushbuf.data() + prevptr, p, bytes) == 0) { - commands.push_back({type, bytes, prevptr}); + commands.push_back({ type, bytes, prevptr }); // Okay, that was easy. Bail out. return; } @@ -474,7 +448,7 @@ static void EmitTextureData(int level, u32 texaddr) { } } -static void FlushPrimState(int vcount) { +void Recorder::FlushPrimState(int vcount) { // TODO: Eventually, how do we handle texturing from framebuf/zbuf? // TODO: Do we need to preload color/depth/stencil (in case from last frame)? @@ -510,7 +484,7 @@ static void FlushPrimState(int vcount) { } } -static void EmitTransfer(u32 op) { +void Recorder::EmitTransfer(u32 op) { FlushRegisters(); // This may not make a lot of sense right now, unless it's to a framebuf... @@ -545,7 +519,7 @@ static void EmitTransfer(u32 op) { lastRegisters.push_back(op); } -static void EmitClut(u32 op) { +void Recorder::EmitClut(u32 op) { u32 addr = gstate.getClutAddress(); // Hardware rendering may be using a framebuffer as CLUT. @@ -569,7 +543,7 @@ static void EmitClut(u32 op) { ClutAddrData data{ addr, flags }; FlushRegisters(); - Command cmd{CommandType::CLUTADDR, sizeof(data), (u32)pushbuf.size()}; + Command cmd{ CommandType::CLUTADDR, sizeof(data), (u32)pushbuf.size() }; pushbuf.resize(pushbuf.size() + sizeof(data)); memcpy(pushbuf.data() + cmd.ptr, &data, sizeof(data)); commands.push_back(cmd); @@ -583,14 +557,14 @@ static void EmitClut(u32 op) { lastRegisters.push_back(op); } -static void EmitPrim(u32 op) { +void Recorder::EmitPrim(u32 op) { FlushPrimState(op & 0x0000FFFF); lastRegisters.push_back(op); DirtyDrawnVRAM(); } -static void EmitBezierSpline(u32 op) { +void Recorder::EmitBezierSpline(u32 op) { int ucount = op & 0xFF; int vcount = (op >> 8) & 0xFF; FlushPrimState(ucount * vcount); @@ -599,15 +573,7 @@ static void EmitBezierSpline(u32 op) { DirtyDrawnVRAM(); } -bool IsActive() { - return active; -} - -bool IsActivePending() { - return nextFrame || active; -} - -bool RecordNextFrame(const std::function callback) { +bool Recorder::RecordNextFrame(const std::function callback) { if (!nextFrame) { flipLastAction = gpuStats.numFlips; flipFinishAt = -1; @@ -618,12 +584,7 @@ bool RecordNextFrame(const std::function callback) { return false; } -void ClearCallback() { - // Not super thread safe.. - writeCallback = nullptr; -} - -static void FinishRecording() { +void Recorder::FinishRecording() { // We're done - this was just to write the result out. if (!active) { return; @@ -646,7 +607,7 @@ static void FinishRecording() { writeCallback = nullptr; } -static void CheckEdramTrans() { +void Recorder::CheckEdramTrans() { if (!gpuDebug) return; @@ -656,13 +617,13 @@ static void CheckEdramTrans() { lastEdramTrans = value; FlushRegisters(); - Command cmd{CommandType::EDRAMTRANS, sizeof(value), (u32)pushbuf.size()}; + Command cmd{ CommandType::EDRAMTRANS, sizeof(value), (u32)pushbuf.size() }; pushbuf.resize(pushbuf.size() + sizeof(value)); memcpy(pushbuf.data() + cmd.ptr, &value, sizeof(value)); commands.push_back(cmd); } -void NotifyCommand(u32 pc) { +void Recorder::NotifyCommand(u32 pc) { if (!active) { return; } @@ -716,7 +677,7 @@ void NotifyCommand(u32 pc) { } } -void NotifyMemcpy(u32 dest, u32 src, u32 sz) { +void Recorder::NotifyMemcpy(u32 dest, u32 src, u32 sz) { if (!active) { return; } @@ -724,7 +685,7 @@ void NotifyMemcpy(u32 dest, u32 src, u32 sz) { CheckEdramTrans(); if (Memory::IsVRAMAddress(dest)) { FlushRegisters(); - Command cmd{CommandType::MEMCPYDEST, sizeof(dest), (u32)pushbuf.size()}; + Command cmd{ CommandType::MEMCPYDEST, sizeof(dest), (u32)pushbuf.size() }; pushbuf.resize(pushbuf.size() + sizeof(dest)); memcpy(pushbuf.data() + cmd.ptr, &dest, sizeof(dest)); commands.push_back(cmd); @@ -738,7 +699,7 @@ void NotifyMemcpy(u32 dest, u32 src, u32 sz) { } } -void NotifyMemset(u32 dest, int v, u32 sz) { +void Recorder::NotifyMemset(u32 dest, int v, u32 sz) { if (!active) { return; } @@ -752,10 +713,10 @@ void NotifyMemset(u32 dest, int v, u32 sz) { if (Memory::IsVRAMAddress(dest)) { sz = Memory::ValidSize(dest, sz); - MemsetCommand data{dest, v, sz}; + MemsetCommand data{ dest, v, sz }; FlushRegisters(); - Command cmd{CommandType::MEMSET, sizeof(data), (u32)pushbuf.size()}; + Command cmd{ CommandType::MEMSET, sizeof(data), (u32)pushbuf.size() }; pushbuf.resize(pushbuf.size() + sizeof(data)); memcpy(pushbuf.data() + cmd.ptr, &data, sizeof(data)); commands.push_back(cmd); @@ -764,12 +725,12 @@ void NotifyMemset(u32 dest, int v, u32 sz) { } } -void NotifyUpload(u32 dest, u32 sz) { +void Recorder::NotifyUpload(u32 dest, u32 sz) { // This also checks the edram translation value and dirties VRAM. NotifyMemcpy(dest, dest, sz); } -static bool HasDrawCommands() { +bool Recorder::HasDrawCommands() const { if (commands.empty()) return false; @@ -788,7 +749,7 @@ static bool HasDrawCommands() { return false; } -void NotifyDisplay(u32 framebuf, int stride, int fmt) { +void Recorder::NotifyDisplay(u32 framebuf, int stride, int fmt) { bool writePending = false; if (active && HasDrawCommands()) { writePending = true; @@ -823,7 +784,7 @@ void NotifyDisplay(u32 framebuf, int stride, int fmt) { } } -void NotifyBeginFrame() { +void Recorder::NotifyBeginFrame() { const bool noDisplayAction = flipLastAction + 4 < gpuStats.numFlips; // We do this only to catch things that don't call NotifyDisplay. if (active && HasDrawCommands() && (noDisplayAction || gpuStats.numFlips == flipFinishAt)) { @@ -856,7 +817,7 @@ void NotifyBeginFrame() { } } -void NotifyCPU() { +void Recorder::NotifyCPU() { if (!active) { return; } @@ -864,4 +825,4 @@ void NotifyCPU() { DirtyAllVRAM(DirtyVRAMFlag::UNKNOWN); } -}; +} // namespace GPURecord diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index 454fc4926c43..514ca1c04c52 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -18,24 +18,92 @@ #pragma once #include +#include +#include +#include #include "Common/CommonTypes.h" +#include "GPU/Debugger/RecordFormat.h" class Path; namespace GPURecord { -bool IsActive(); -bool IsActivePending(); -bool RecordNextFrame(const std::function callback); -void ClearCallback(); +constexpr uint32_t DIRTY_VRAM_SHIFT = 8; +constexpr uint32_t DIRTY_VRAM_ROUND = (1 << DIRTY_VRAM_SHIFT) - 1; +constexpr uint32_t DIRTY_VRAM_SIZE = (2 * 1024 * 1024) >> DIRTY_VRAM_SHIFT; +constexpr uint32_t DIRTY_VRAM_MASK = (2 * 1024 * 1024 - 1) >> DIRTY_VRAM_SHIFT; +enum class DirtyVRAMFlag : uint8_t { + CLEAN = 0, + UNKNOWN = 1, + DIRTY = 2, + DRAWN = 3, +}; + +class Recorder { +public: + bool IsActive() const { + return active; + } + bool IsActivePending() const { + return nextFrame || active; + } + bool RecordNextFrame(const std::function callback); + void ClearCallback() { + // Not super thread safe.. + writeCallback = nullptr; + } + + void NotifyCommand(u32 pc); + void NotifyMemcpy(u32 dest, u32 src, u32 sz); + void NotifyMemset(u32 dest, int v, u32 sz); + void NotifyUpload(u32 dest, u32 sz); + void NotifyDisplay(u32 addr, int stride, int fmt); + void NotifyBeginFrame(); + void NotifyCPU(); +private: + void FlushRegisters(); + void DirtyAllVRAM(DirtyVRAMFlag flag); + void DirtyVRAM(u32 start, u32 sz, DirtyVRAMFlag flag); + void DirtyDrawnVRAM(); + + bool BeginRecording(); + Path WriteRecording(); + + bool HasDrawCommands() const; + void CheckEdramTrans(); + void FinishRecording(); + + Command EmitCommandWithRAM(CommandType t, const void *p, u32 sz, u32 align); -void NotifyCommand(u32 pc); -void NotifyMemcpy(u32 dest, u32 src, u32 sz); -void NotifyMemset(u32 dest, int v, u32 sz); -void NotifyUpload(u32 dest, u32 sz); -void NotifyDisplay(u32 addr, int stride, int fmt); -void NotifyBeginFrame(); -void NotifyCPU(); + void UpdateLastVRAM(u32 addr, u32 bytes); + void ClearLastVRAM(u32 addr, u8 c, u32 bytes); + int CompareLastVRAM(u32 addr, u32 bytes) const; + u32 GetTargetFlags(u32 addr, u32 sizeInRAM); + + void FlushPrimState(int vcount); + void EmitTextureData(int level, u32 texaddr); + void EmitTransfer(u32 op); + void EmitClut(u32 op); + void EmitPrim(u32 op); + void EmitBezierSpline(u32 op); + + bool active = false; + std::atomic nextFrame = false; + int flipLastAction = -1; + int flipFinishAt = -1; + uint32_t lastEdramTrans = 0x400; + std::function writeCallback; + + std::vector pushbuf; + std::vector commands; + std::vector lastRegisters; + std::vector lastTextures; + std::set lastRenderTargets; + std::vector lastVRAM; + + DirtyVRAMFlag dirtyVRAM[DIRTY_VRAM_SIZE]; }; + +} // namespace GPURecord diff --git a/GPU/Debugger/State.h b/GPU/Debugger/State.h index aed98e897e33..a4bfbcc435f2 100644 --- a/GPU/Debugger/State.h +++ b/GPU/Debugger/State.h @@ -41,10 +41,7 @@ void FormatStateRow(GPUDebugInterface *debug, char *dest, size_t destSize, CmdFo void FormatVertCol(char *dest, size_t destSize, const GPUDebugVertex &vert, int col); void FormatVertColRaw(VertexDecoder *decoder, char *dest, size_t destSize, int row, int col); - // These are utilities used by the debugger vertex preview. - -// Later I hope to re-use more of the real logic. bool GetPrimPreview(u32 op, GEPrimitiveType &prim, std::vector &vertices, std::vector &indices, int &count); void DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]); void DescribePixelRGBA(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index 4395a3275a5e..ff15a566a647 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -186,7 +186,7 @@ bool ProcessStepping() { return true; } -bool EnterStepping() { +bool EnterStepping(CoreState coreState) { _dbg_assert_(gpuDebug); std::unique_lock guard(pauseLock); @@ -216,7 +216,7 @@ bool EnterStepping() { pauseAction = PAUSE_BREAK; } - coreState = CORE_STEPPING_GE; + ::coreState = CORE_STEPPING_GE; return true; } diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index 0146053a8aa8..9c01f0836339 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -27,7 +27,7 @@ namespace GPUStepping { // Should be called from the emu thread. // Begins stepping and increments the stepping counter while inside a lock. - bool EnterStepping(); + bool EnterStepping(CoreState coreState); bool IsStepping(); void ResumeFromStepping(); diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 6b8e8bdca002..50f830b385d4 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -421,7 +421,7 @@ void DrawEngineDX9::DoFlush() { ResetAfterDrawInline(); framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); - GPUDebug::NotifyDraw(); + GPUDebug::NotifyFlush(); } void TessellationDataTransferDX9::SendDataToShader(const SimpleVertex *const *points, int size_u, int size_v, u32 vertType, const Spline::Weight2D &weights) { diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 0e3090752b1e..08df5d630bd4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -451,7 +451,7 @@ void DrawEngineGLES::DoFlush() { bail: ResetAfterDrawInline(); framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); - GPUDebug::NotifyDraw(); + GPUDebug::NotifyFlush(); } // TODO: Refactor this to a single USE flag. diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index ec29fbad3214..304173f064e4 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -625,17 +625,17 @@ void GPUCommon::PSPFrame() { dumpThisFrame_ = false; } GPUDebug::NotifyBeginFrame(); - GPURecord::NotifyBeginFrame(); + recorder_.NotifyBeginFrame(); } // Returns false on breakpoint. bool GPUCommon::SlowRunLoop(DisplayList &list) { const bool dumpThisFrame = dumpThisFrame_; while (downcount > 0) { - GPUDebug::NotifyResult result = GPUDebug::NotifyCommand(list.pc); + GPUDebug::NotifyResult result = GPUDebug::NotifyCommand(list.pc, &breakpoints_); if (result == GPUDebug::NotifyResult::Execute) { - GPURecord::NotifyCommand(list.pc); + recorder_.NotifyCommand(list.pc); u32 op = Memory::ReadUnchecked_U32(list.pc); u32 cmd = op >> 24; @@ -740,8 +740,7 @@ int GPUCommon::GetNextListIndex() { } } -// This is now called when coreState == CORE_RUNNING_GE. -// TODO: It should return the next action.. (break into debugger or continue running) +// This is now called when coreState == CORE_RUNNING_GE, in addition to from the various sceGe commands. DLResult GPUCommon::ProcessDLQueue() { if (!resumingFromDebugBreak_) { startingTicks = CoreTiming::GetTicks(); @@ -791,7 +790,8 @@ DLResult GPUCommon::ProcessDLQueue() { gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING; // To enable breakpoints, we don't do fast matrix loads while debugger active. - debugRecording_ = GPUDebug::IsActive() || GPURecord::IsActive(); + debugRecording_ = recorder_.IsActive(); + useFastRunLoop_ = !(dumpThisFrame_ || debugRecording_ || GPUDebug::NeedsSlowInterpreter() || breakpoints_.HasBreakpoints()); } else { resumingFromDebugBreak_ = false; // The bottom part of the gpuState loop below, that wasn't executed @@ -804,7 +804,7 @@ DLResult GPUCommon::ProcessDLQueue() { // Proceed... } - const bool useFastRunLoop = !dumpThisFrame_ && !debugRecording_; + const bool useFastRunLoop = useFastRunLoop_; while (gpuState == GPUSTATE_RUNNING) { if (list.pc == list.stall) { @@ -821,7 +821,7 @@ DLResult GPUCommon::ProcessDLQueue() { // Hit a breakpoint, so we set the state and bail. We can resume later. // TODO: Cycle counting might need some more care? FinishDeferred(); - _dbg_assert_(!GPURecord::IsActive()); + _dbg_assert_(!recorder_.IsActive()); resumingFromDebugBreak_ = true; return DLResult::DebugBreak; @@ -837,7 +837,7 @@ DLResult GPUCommon::ProcessDLQueue() { FinishDeferred(); if (debugRecording_) - GPURecord::NotifyCPU(); + recorder_.NotifyCPU(); // We haven't run the op at list.pc, so it shouldn't count. if (cycleLastPC != list.pc) { @@ -880,9 +880,9 @@ DLResult GPUCommon::ProcessDLQueue() { } bool GPUCommon::ShouldSplitOverGe() const { - // Check for debugger active, etc. + // Check for debugger active. // We only need to do this if we want to be able to step through Ge display lists using the Ge debuggers. - return GPUDebug::IsActive() || g_Config.bShowImDebugger; + return GPUDebug::NeedsSlowInterpreter() || breakpoints_.HasBreakpoints(); } void GPUCommon::Execute_OffsetAddr(u32 op, u32 diff) { @@ -948,8 +948,8 @@ void GPUCommon::DoExecuteCall(u32 target) { DisplayList *currentList = this->currentList; // Bone matrix optimization - many games will CALL a bone matrix (!). - // We don't optimize during recording - so the matrix data gets recorded. - if (!debugRecording_ && Memory::IsValidRange(target, 13 * 4) && (Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA) { + // We don't optimize during recording or debugging - so the matrix data gets recorded. + if (useFastRunLoop_ && Memory::IsValidRange(target, 13 * 4) && (Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA) { // Check for the end if ((Memory::ReadUnchecked_U32(target + 11 * 4) >> 24) == GE_CMD_BONEMATRIXDATA && (Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET && @@ -1944,7 +1944,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags } InvalidateCache(dest, size, GPU_INVALIDATE_HINT); if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED)) - GPURecord::NotifyMemcpy(dest, src, size); + recorder_.NotifyMemcpy(dest, src, size); return false; } @@ -1961,7 +1961,7 @@ bool GPUCommon::PerformMemorySet(u32 dest, u8 v, int size) { NotifyMemInfo(MemBlockFlags::WRITE, dest, size, "GPUMemset"); // Or perhaps a texture, let's invalidate. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); - GPURecord::NotifyMemset(dest, v, size); + recorder_.NotifyMemset(dest, v, size); return false; } @@ -1974,7 +1974,7 @@ bool GPUCommon::PerformReadbackToMemory(u32 dest, int size) { bool GPUCommon::PerformWriteColorFromMemory(u32 dest, int size) { if (Memory::IsVRAMAddress(dest)) { - GPURecord::NotifyUpload(dest, size); + recorder_.NotifyUpload(dest, size); return PerformMemoryCopy(dest, dest, size, GPUCopyFlag::FORCE_SRC_MATCH_MEM | GPUCopyFlag::DEBUG_NOTIFIED); } return false; diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index cf442ed61c37..f929db8febf0 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -12,6 +12,8 @@ #include "GPU/GPU.h" #include "GPU/GPUCommon.h" #include "GPU/GPUState.h" +#include "GPU/Debugger/Record.h" +#include "GPU/Debugger/Breakpoints.h" #include "GPU/Common/ShaderCommon.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/GPUDefinitions.h" @@ -377,6 +379,13 @@ class GPUCommon : public GPUDebugInterface { void PSPFrame(); + GPURecord::Recorder *GetRecorder() override { + return &recorder_; + } + GPUBreakpoints *GetBreakpoints() override { + return &breakpoints_; + } + protected: virtual void ClearCacheNextFrame() {} @@ -447,7 +456,7 @@ class GPUCommon : public GPUDebugInterface { bool interruptRunning = false; GPURunState gpuState = GPUSTATE_RUNNING; - bool isbreak; + bool isbreak; // This doesn't mean debugger breakpoints. u64 drawCompleteTicks; u64 busyTicks; @@ -459,8 +468,9 @@ class GPUCommon : public GPUDebugInterface { bool resumingFromDebugBreak_ = false; bool dumpNextFrame_ = false; bool dumpThisFrame_ = false; - bool debugRecording_; - bool interruptsEnabled_; + bool useFastRunLoop_ = false; + bool debugRecording_ = false; + bool interruptsEnabled_ = false; bool displayResized_ = false; bool renderResized_ = false; bool configChanged_ = false; @@ -500,6 +510,9 @@ class GPUCommon : public GPUDebugInterface { std::string reportingPrimaryInfo_; std::string reportingFullInfo_; + GPURecord::Recorder recorder_; + GPUBreakpoints breakpoints_; + private: void DoExecuteCall(u32 target); void PopDLQueue(); diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 4616b34b8a33..951c78c373f0 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -512,7 +512,7 @@ void GPUCommonHW::BeginHostFrame() { } void GPUCommonHW::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { - framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format); + framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format, &recorder_); } void GPUCommonHW::CheckFlushOp(int cmd, u32 diff) { @@ -1457,7 +1457,7 @@ void GPUCommonHW::Execute_TexLevel(u32 op, u32 diff) { void GPUCommonHW::Execute_LoadClut(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); - textureCache_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); + textureCache_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes(), &recorder_); } diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index d0b0b8d035dc..e58d9adc12c7 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -494,7 +494,7 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for displayStride_ = stride; displayFormat_ = format; GPUDebug::NotifyDisplay(framebuf, stride, format); - GPURecord::NotifyDisplay(framebuf, stride, format); + recorder_.NotifyDisplay(framebuf, stride, format); } DSStretch g_DarkStalkerStretch; @@ -1305,7 +1305,7 @@ bool SoftGPU::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags) // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED)) - GPURecord::NotifyMemcpy(dest, src, size); + recorder_.NotifyMemcpy(dest, src, size); // Let's just be safe. MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY); return false; @@ -1315,7 +1315,7 @@ bool SoftGPU::PerformMemorySet(u32 dest, u8 v, int size) { // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); - GPURecord::NotifyMemset(dest, v, size); + recorder_.NotifyMemset(dest, v, size); // Let's just be safe. MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY); return false; @@ -1332,7 +1332,7 @@ bool SoftGPU::PerformWriteColorFromMemory(u32 dest, int size) { // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); - GPURecord::NotifyUpload(dest, size); + recorder_.NotifyUpload(dest, size); return false; } diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index b92cb7c1beb4..2a8e4c72ed82 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -896,7 +896,7 @@ void TransformUnit::Flush(const char *reason) { return; binner_->Flush(reason); - GPUDebug::NotifyDraw(); + GPUDebug::NotifyFlush(); hasDraws_ = false; } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 3285ef13d561..47b466fc11ee 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -569,7 +569,7 @@ void DrawEngineVulkan::DoFlush() { framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); - GPUDebug::NotifyDraw(); + GPUDebug::NotifyFlush(); } void DrawEngineVulkan::ResetAfterDraw() { diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index eeb7df2f1485..fd0ea6e1855e 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -153,7 +153,7 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { }); items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) { - GPURecord::RecordNextFrame([](const Path &dumpPath) { + gpuDebug->GetRecorder()->RecordNextFrame([](const Path &dumpPath) { NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { System_ShowFileInFolder(dumpPath); diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index 33638556782c..05cf4a376ec0 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -129,16 +129,15 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { stallAddr = displayList.stall; } - if (pc != 0xFFFFFFFF) { - if (gotoPC_) { - selectedAddr_ = pc; - gotoPC_ = false; - } + if (pc != 0xFFFFFFFF && gotoPC_) { + selectedAddr_ = pc; + gotoPC_ = false; } float pcY = canvas_p0.y + ((pc - windowStartAddr) / instrWidth) * lineHeight; draw_list->AddRectFilled(ImVec2(canvas_p0.x, pcY), ImVec2(canvas_p1.x, pcY + lineHeight), IM_COL32(0x10, 0x70, 0x10, 255)); - + float stallY = canvas_p0.y + ((stallAddr - windowStartAddr) / instrWidth) * lineHeight; + draw_list->AddRectFilled(ImVec2(canvas_p0.x, stallY), ImVec2(canvas_p1.x, stallY + lineHeight), IM_COL32(0x70, 0x20, 0x10, 255)); u32 addr = windowStartAddr; for (int line = 0; line < numLines; line++) { char addrBuffer[128]; @@ -161,7 +160,7 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { // draw_list->AddText(liveStart, 0xFFFFFFFF, disMeta.liveInfo); // } - bool bp = GPUBreakpoints::IsAddressBreakpoint(addr); + bool bp = gpuDebug->GetBreakpoints()->IsAddressBreakpoint(addr); if (bp) { draw_list->AddCircleFilled(ImVec2(canvas_p0.x + lineHeight * 0.5f, lineStart.y + lineHeight * 0.5f), lineHeight * 0.45f, 0xFF0000FF, 12); } @@ -190,10 +189,10 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { if (pressed) { if (io.MousePos.x < canvas_p0.x + lineHeight) { // Toggle breakpoint - if (!GPUBreakpoints::IsAddressBreakpoint(dragAddr_)) { - GPUBreakpoints::AddAddressBreakpoint(dragAddr_); + if (!gpuDebug->GetBreakpoints()->IsAddressBreakpoint(dragAddr_)) { + gpuDebug->GetBreakpoints()->AddAddressBreakpoint(dragAddr_); } else { - GPUBreakpoints::RemoveAddressBreakpoint(dragAddr_); + gpuDebug->GetBreakpoints()->RemoveAddressBreakpoint(dragAddr_); } bpPopup_ = true; } else { @@ -209,7 +208,7 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { if (ImGui::BeginPopup("context")) { if (bpPopup_) { if (ImGui::MenuItem("Remove breakpoint", NULL, false)) { - GPUBreakpoints::RemoveAddressBreakpoint(dragAddr_); + gpuDebug->GetBreakpoints()->RemoveAddressBreakpoint(dragAddr_); } } else if (Memory::IsValid4AlignedAddress(dragAddr_)) { char buffer[64]; @@ -367,27 +366,27 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa } ImGui::SameLine(); if (ImGui::Button("Tex")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX, gpuDebug->GetBreakpoints()); } ImGui::SameLine(); if (ImGui::Button("NonTex")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONTEX); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONTEX, gpuDebug->GetBreakpoints()); } ImGui::SameLine(); if (ImGui::Button("Prim")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::PRIM); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::PRIM, gpuDebug->GetBreakpoints()); } ImGui::SameLine(); if (ImGui::Button("Draw")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::DRAW); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::DRAW, gpuDebug->GetBreakpoints()); } ImGui::SameLine(); if (ImGui::Button("Curve")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::CURVE); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::CURVE, gpuDebug->GetBreakpoints()); } ImGui::SameLine(); if (ImGui::Button("Single step")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP, gpuDebug->GetBreakpoints()); } if (disableStepButtons) { ImGui::EndDisabled(); @@ -415,7 +414,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa ImGui::Text("Step pending (waiting for CPU): %s", GPUDebug::BreakNextToString(GPUDebug::GetBreakNext())); ImGui::SameLine(); if (ImGui::Button("Cancel step")) { - GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONE); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONE, gpuDebug->GetBreakpoints()); } } } else { @@ -431,7 +430,7 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa const auto &list = gpuDebug->GetDisplayList(index); char title[64]; snprintf(title, sizeof(title), "List %d", list.id); - if (ImGui::CollapsingHeader(title)) { + if (ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Text("State: %s", DLStateToString(list.state)); ImGui::TextUnformatted("PC:"); ImGui::SameLine(); @@ -442,6 +441,11 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa if (list.pendingInterrupt) { ImGui::TextUnformatted("(Pending interrupt)"); } + if (list.stall) { + ImGui::TextUnformatted("Stall addr:"); + ImGui::SameLine(); + ImClickableAddress(list.pc, control, ImCmd::SHOW_IN_GE_DISASM); + } ImGui::Text("Stack depth: %d", (int)list.stackptr); ImGui::Text("BBOX result: %d", (int)list.bboxResult); } diff --git a/UI/ImDebugger/ImGe.h b/UI/ImDebugger/ImGe.h index 895ba8964579..3f11702c37e9 100644 --- a/UI/ImDebugger/ImGe.h +++ b/UI/ImDebugger/ImGe.h @@ -35,7 +35,7 @@ class ImGeDisasmView { void NotifyStep(); private: - u32 selectedAddr_ = INVALID_ADDR; + u32 selectedAddr_ = 0; u32 dragAddr_ = INVALID_ADDR; bool bpPopup_ = false; bool gotoPC_ = false; diff --git a/Windows/GEDebugger/CtrlDisplayListView.cpp b/Windows/GEDebugger/CtrlDisplayListView.cpp index 113c7f297a02..3c7e7c4129f4 100644 --- a/Windows/GEDebugger/CtrlDisplayListView.cpp +++ b/Windows/GEDebugger/CtrlDisplayListView.cpp @@ -217,7 +217,7 @@ void CtrlDisplayListView::onPaint(WPARAM wParam, LPARAM lParam) DeleteObject(backgroundPen); // display address/symbol - if (GPUBreakpoints::IsAddressBreakpoint(address)) + if (gpuDebug->GetBreakpoints()->IsAddressBreakpoint(address)) { textColor = 0x0000FF; int yOffset = std::max(-1,(rowHeight-14+1)/2); @@ -269,12 +269,12 @@ void CtrlDisplayListView::toggleBreakpoint() void CtrlDisplayListView::PromptBreakpointCond() { std::string expression; - GPUBreakpoints::GetAddressBreakpointCond(curAddress, &expression); + gpuDebug->GetBreakpoints()->GetAddressBreakpointCond(curAddress, &expression); if (!InputBox_GetString(GetModuleHandle(NULL), wnd, L"Expression", expression, expression)) return; std::string error; - if (!GPUBreakpoints::SetAddressBreakpointCond(curAddress, expression, &error)) + if (!gpuDebug->GetBreakpoints()->SetAddressBreakpointCond(curAddress, expression, &error)) MessageBox(wnd, ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION); } @@ -318,7 +318,7 @@ void CtrlDisplayListView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) if (button == 2) { HMENU menu = GetContextMenu(ContextMenuID::DISPLAYLISTVIEW); - EnableMenuItem(menu, ID_GEDBG_SETCOND, GPUBreakpoints::IsAddressBreakpoint(curAddress) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(menu, ID_GEDBG_SETCOND, gpuDebug->GetBreakpoints()->IsAddressBreakpoint(curAddress) ? MF_ENABLED : MF_GRAYED); switch (TriggerContextMenu(ContextMenuID::DISPLAYLISTVIEW, wnd, ContextPoint::FromEvent(lParam))) { diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 2878ad186734..9167530b0437 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -56,7 +56,6 @@ #include "GPU/Debugger/State.h" #include "GPU/Debugger/Stepping.h" -using namespace GPUBreakpoints; using namespace GPUStepping; enum PrimaryDisplayType { @@ -136,7 +135,7 @@ StepCountDlg::~StepCountDlg() { void StepCountDlg::Jump(int count, bool relative) { if (relative && count == 0) return; - GPUDebug::SetBreakNext(GPUDebug::BreakNext::COUNT); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::COUNT, gpuDebug->GetBreakpoints()); GPUDebug::SetBreakCount(count, relative); }; @@ -680,9 +679,9 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) { bufferResult = GPU_GetCurrentTexture(primaryBuffer_, textureLevel_, &primaryIsFramebuffer_); flags = TexturePreviewFlags(state); if (bufferResult) { - UpdateLastTexture(state.getTextureAddress(textureLevel_)); + gpuDebug->GetBreakpoints()->UpdateLastTexture(state.getTextureAddress(textureLevel_)); } else { - UpdateLastTexture((u32)-1); + gpuDebug->GetBreakpoints()->UpdateLastTexture((u32)-1); } } else { switch (PrimaryDisplayType(fbTabs->CurrentTabIndex())) { @@ -733,9 +732,9 @@ void CGEDebugger::UpdateSecondPreview(const GPUgstate &state) { } else { bufferResult = GPU_GetCurrentTexture(secondBuffer_, textureLevel_, &secondIsFramebuffer_); if (bufferResult) { - UpdateLastTexture(state.getTextureAddress(textureLevel_)); + gpuDebug->GetBreakpoints()->UpdateLastTexture(state.getTextureAddress(textureLevel_)); } else { - UpdateLastTexture((u32)-1); + gpuDebug->GetBreakpoints()->UpdateLastTexture((u32)-1); } } @@ -925,8 +924,6 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { return TRUE; case WM_CLOSE: - GPUDebug::SetActive(false); - stepCountDlg.Show(false); Show(false); return TRUE; @@ -978,7 +975,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case IDC_GEDBG_FBTABS: fbTabs->HandleNotify(lParam); - if (GPUDebug::IsActive() && gpuDebug != nullptr) { + if (gpuDebug != nullptr) { UpdatePreviews(); } break; @@ -992,31 +989,31 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_GEDBG_STEPDRAW: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::DRAW); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::DRAW, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEP: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEPTEX: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::TEX, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEPFRAME: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::FRAME, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEPVSYNC: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::VSYNC); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::VSYNC, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEPPRIM: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::PRIM); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::PRIM, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEPCURVE: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::CURVE); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::CURVE, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_STEPCOUNT: @@ -1025,7 +1022,6 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case IDC_GEDBG_BREAKTEX: { - GPUDebug::SetActive(true); if (!gpuDebug) { break; } @@ -1033,10 +1029,10 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { u32 texAddr = state.getTextureAddress(textureLevel_); // TODO: Better interface that allows add/remove or something. if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, L"Texture Address", texAddr, texAddr)) { - if (IsTextureBreakpoint(texAddr)) { - RemoveTextureBreakpoint(texAddr); + if (gpuDebug->GetBreakpoints()->IsTextureBreakpoint(texAddr)) { + gpuDebug->GetBreakpoints()->RemoveTextureBreakpoint(texAddr); } else { - AddTextureBreakpoint(texAddr); + gpuDebug->GetBreakpoints()->AddTextureBreakpoint(texAddr); } } } @@ -1044,7 +1040,6 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case IDC_GEDBG_BREAKTARGET: { - GPUDebug::SetActive(true); if (!gpuDebug) { break; } @@ -1052,25 +1047,25 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { u32 fbAddr = state.getFrameBufRawAddress(); // TODO: Better interface that allows add/remove or something. if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, L"Framebuffer Address", fbAddr, fbAddr)) { - if (IsRenderTargetBreakpoint(fbAddr)) { - RemoveRenderTargetBreakpoint(fbAddr); + if (gpuDebug->GetBreakpoints()->IsRenderTargetBreakpoint(fbAddr)) { + gpuDebug->GetBreakpoints()->RemoveRenderTargetBreakpoint(fbAddr); } else { - AddRenderTargetBreakpoint(fbAddr); + gpuDebug->GetBreakpoints()->AddRenderTargetBreakpoint(fbAddr); } } } break; case IDC_GEDBG_TEXLEVELDOWN: - UpdateTextureLevel(textureLevel_ - 1); - if (GPUDebug::IsActive() && gpuDebug != nullptr) { + if (gpuDebug != nullptr) { + UpdateTextureLevel(textureLevel_ - 1); UpdatePreviews(); } break; case IDC_GEDBG_TEXLEVELUP: - UpdateTextureLevel(textureLevel_ + 1); - if (GPUDebug::IsActive() && gpuDebug != nullptr) { + if (gpuDebug != nullptr) { + UpdateTextureLevel(textureLevel_ + 1); UpdatePreviews(); } break; @@ -1083,18 +1078,18 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L""); SetDlgItemText(m_hDlg, IDC_GEDBG_PRIMCOUNTER, L""); - GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONE); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::NONE, gpuDebug->GetBreakpoints()); break; case IDC_GEDBG_RECORD: - GPURecord::RecordNextFrame([](const Path &path) { + gpuDebug->GetRecorder()->RecordNextFrame([](const Path &path) { // Opens a Windows Explorer window with the file, when done. System_ShowFileInFolder(path); }); break; case IDC_GEDBG_FLUSH: - if (GPUDebug::IsActive() && gpuDebug != nullptr) { + if (gpuDebug != nullptr) { if (!autoFlush_) GPU_FlushDrawing(); UpdatePreviews(); @@ -1106,14 +1101,14 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case IDC_GEDBG_FORCEOPAQUE: - if (GPUDebug::IsActive() && gpuDebug != nullptr) { + if (gpuDebug != nullptr) { forceOpaque_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_FORCEOPAQUE), BM_GETCHECK, 0, 0) != 0; UpdatePreviews(); } break; case IDC_GEDBG_SHOWCLUT: - if (GPUDebug::IsActive() && gpuDebug != nullptr) { + if (gpuDebug != nullptr) { showClut_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_SHOWCLUT), BM_GETCHECK, 0, 0) != 0; UpdatePreviews(); } @@ -1131,34 +1126,32 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case WM_GEDBG_STEPDISPLAYLIST: - GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP); + GPUDebug::SetBreakNext(GPUDebug::BreakNext::OP, gpuDebug->GetBreakpoints()); break; case WM_GEDBG_TOGGLEPCBREAKPOINT: { - GPUDebug::SetActive(true); u32 pc = (u32)wParam; bool temp; - bool isBreak = IsAddressBreakpoint(pc, temp); + bool isBreak = gpuDebug->GetBreakpoints()->IsAddressBreakpoint(pc, temp); if (isBreak && !temp) { - if (GetAddressBreakpointCond(pc, nullptr)) { + if (gpuDebug->GetBreakpoints()->GetAddressBreakpointCond(pc, nullptr)) { int ret = MessageBox(m_hDlg, L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO); if (ret == IDYES) - RemoveAddressBreakpoint(pc); + gpuDebug->GetBreakpoints()->RemoveAddressBreakpoint(pc); } else { - RemoveAddressBreakpoint(pc); + gpuDebug->GetBreakpoints()->RemoveAddressBreakpoint(pc); } } else { - AddAddressBreakpoint(pc); + gpuDebug->GetBreakpoints()->AddAddressBreakpoint(pc); } } break; case WM_GEDBG_RUNTOWPARAM: { - GPUDebug::SetActive(true); u32 pc = (u32)wParam; - AddAddressBreakpoint(pc, true); + gpuDebug->GetBreakpoints()->AddAddressBreakpoint(pc, true); SendMessage(m_hDlg,WM_COMMAND,IDC_GEDBG_RESUME,0); } break; diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 01c224058fd7..237ab0f379f3 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -38,8 +38,6 @@ #include "GPU/Debugger/Stepping.h" #include "GPU/Debugger/State.h" -using namespace GPUBreakpoints; - // First column is the breakpoint icon. static const GenericListViewColumn stateValuesCols[] = { { L"", 0.03f }, @@ -334,12 +332,12 @@ void CtrlStateValues::OnDoubleClick(int row, int column) { if (column == STATEVALUES_COL_BREAKPOINT) { bool proceed = true; - if (GetCmdBreakpointCond(info.cmd, nullptr)) { + if (gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info.cmd, nullptr)) { int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO); proceed = ret == IDYES; } if (proceed) - SetItemState(row, ToggleBreakpoint(info) ? 1 : 0); + SetItemState(row, gpuDebug->GetBreakpoints()->ToggleCmdBreakpoint(info) ? 1 : 0); return; } @@ -401,7 +399,7 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) { HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_STATE); SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE); - EnableMenuItem(subMenu, ID_GEDBG_SETCOND, GPUBreakpoints::IsCmdBreakpoint(info.cmd) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(subMenu, ID_GEDBG_SETCOND, gpuDebug->GetBreakpoints()->GPUBreakpoints::IsCmdBreakpoint(info.cmd) ? MF_ENABLED : MF_GRAYED); // Ehh, kinda ugly. if (!watchList.empty() && rows_ == &watchList[0]) { @@ -419,12 +417,12 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) { { case ID_DISASM_TOGGLEBREAKPOINT: { bool proceed = true; - if (GetCmdBreakpointCond(info.cmd, nullptr)) { + if (gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info.cmd, nullptr)) { int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO); proceed = ret == IDYES; } if (proceed) - SetItemState(row, ToggleBreakpoint(info) ? 1 : 0); + SetItemState(row, gpuDebug->GetBreakpoints()->ToggleCmdBreakpoint(info) ? 1 : 0); break; } @@ -498,20 +496,19 @@ bool CtrlStateValues::RowValuesChanged(int row) { void CtrlStateValues::PromptBreakpointCond(const GECmdInfo &info) { std::string expression; - GPUBreakpoints::GetCmdBreakpointCond(info.cmd, &expression); + gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info.cmd, &expression); if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression)) return; std::string error; - if (!GPUBreakpoints::SetCmdBreakpointCond(info.cmd, expression, &error)) { + if (!gpuDebug->GetBreakpoints()->SetCmdBreakpointCond(info.cmd, expression, &error)) { MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION); } else { if (info.otherCmd) - GPUBreakpoints::SetCmdBreakpointCond(info.otherCmd, expression, &error); + gpuDebug->GetBreakpoints()->SetCmdBreakpointCond(info.otherCmd, expression, &error); if (info.otherCmd2) - GPUBreakpoints::SetCmdBreakpointCond(info.otherCmd2, expression, &error); + gpuDebug->GetBreakpoints()->SetCmdBreakpointCond(info.otherCmd2, expression, &error); } - } TabStateValues::TabStateValues(const GECommand *rows, size_t rowCount, LPCSTR dialogID, HINSTANCE _hInstance, HWND _hParent) diff --git a/Windows/GEDebugger/TabVertices.cpp b/Windows/GEDebugger/TabVertices.cpp index 5d18b1d9d3f9..464471398e3c 100644 --- a/Windows/GEDebugger/TabVertices.cpp +++ b/Windows/GEDebugger/TabVertices.cpp @@ -437,16 +437,16 @@ void CtrlMatrixList::ToggleBreakpoint(int row) { return; // Okay, this command is in range. Toggle the actual breakpoint. - bool state = !GPUBreakpoints::IsCmdBreakpoint(info->cmd); + bool state = !gpuDebug->GetBreakpoints()->IsCmdBreakpoint(info->cmd); if (state) { - GPUBreakpoints::AddCmdBreakpoint(info->cmd); + gpuDebug->GetBreakpoints()->AddCmdBreakpoint(info->cmd); } else { - if (GPUBreakpoints::GetCmdBreakpointCond(info->cmd, nullptr)) { + if (gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info->cmd, nullptr)) { int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO); if (ret != IDYES) return; } - GPUBreakpoints::RemoveCmdBreakpoint(info->cmd); + gpuDebug->GetBreakpoints()->RemoveCmdBreakpoint(info->cmd); } for (int r = info->row; r < (info + 1)->row; ++r) { @@ -460,12 +460,12 @@ void CtrlMatrixList::PromptBreakpointCond(int row) { return; std::string expression; - GPUBreakpoints::GetCmdBreakpointCond(info->cmd, &expression); + gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info->cmd, &expression); if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression)) return; std::string error; - if (!GPUBreakpoints::SetCmdBreakpointCond(info->cmd, expression, &error)) + if (!gpuDebug->GetBreakpoints()->SetCmdBreakpointCond(info->cmd, expression, &error)) MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION); } @@ -531,7 +531,7 @@ void CtrlMatrixList::OnRightClick(int row, int column, const POINT &point) { HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_MATRIX); SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE); - EnableMenuItem(subMenu, ID_GEDBG_SETCOND, info && GPUBreakpoints::IsCmdBreakpoint(info->cmd) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(subMenu, ID_GEDBG_SETCOND, info && gpuDebug->GetBreakpoints()->IsCmdBreakpoint(info->cmd) ? MF_ENABLED : MF_GRAYED); switch (TriggerContextMenu(ContextMenuID::GEDBG_MATRIX, GetHandle(), ContextPoint::FromClient(point))) { case ID_DISASM_TOGGLEBREAKPOINT: