diff --git a/Core/Core.cpp b/Core/Core.cpp index d843047808..15d1c42d3b 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 30516ba81e..d8a54d2e30 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 609f17dd00..a0ba14ca13 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 cacc866f8f..84f034f9ee 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 37a71d751b..cbd13773dd 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 @@ public: 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 35e607db75..c62c1afd6d 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -34,6 +34,10 @@ class TextureCacheCommon; struct VirtualFramebuffer; struct DisplayList; +namespace GPURecord { +class Recorder; +} + struct GPUDebugOp { u32 pc; u8 cmd; @@ -239,6 +243,8 @@ public: virtual const std::list &GetDisplayListQueue() = 0; virtual const DisplayList &GetDisplayList(int index) = 0; + virtual GPURecord::Recorder *GetRecorder() = 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 84cbf90679..5a8f3bd12d 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 48b29dd359..48fc25dd54 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 @@ public: 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/Debugger/Playback.cpp b/GPU/Debugger/Playback.cpp index 1a2b951bb1..e9122e796d 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 f08b2067aa..67d03452c6 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 454fc4926c..514ca1c04c 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(); - -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(); - +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 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/Stepping.cpp b/GPU/Debugger/Stepping.cpp index 4395a3275a..ff15a566a6 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 0146053a8a..9c01f08363 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/GPUCommon.cpp b/GPU/GPUCommon.cpp index 33c7d2d0d5..6ff2d895c8 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -625,7 +625,7 @@ void GPUCommon::PSPFrame() { dumpThisFrame_ = false; } GPUDebug::NotifyBeginFrame(); - GPURecord::NotifyBeginFrame(); + recorder_.NotifyBeginFrame(); } // Returns false on breakpoint. @@ -635,7 +635,7 @@ bool GPUCommon::SlowRunLoop(DisplayList &list) { GPUDebug::NotifyResult result = GPUDebug::NotifyCommand(list.pc); if (result == GPUDebug::NotifyResult::Execute) { - GPURecord::NotifyCommand(list.pc); + recorder_.NotifyCommand(list.pc); u32 op = Memory::ReadUnchecked_U32(list.pc); u32 cmd = op >> 24; @@ -790,7 +790,7 @@ 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_ = GPURecord::IsActive(); + debugRecording_ = recorder_.IsActive(); useFastRunLoop_ = !(dumpThisFrame_ || debugRecording_ || GPUDebug::NeedsSlowInterpreter()); } else { resumingFromDebugBreak_ = false; @@ -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) { @@ -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 4bdfcaf7a0..999b55d43a 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -12,6 +12,7 @@ #include "GPU/GPU.h" #include "GPU/GPUCommon.h" #include "GPU/GPUState.h" +#include "GPU/Debugger/Record.h" #include "GPU/Common/ShaderCommon.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/GPUDefinitions.h" @@ -377,6 +378,10 @@ public: void PSPFrame(); + GPURecord::Recorder *GetRecorder() override { + return &recorder_; + } + protected: virtual void ClearCacheNextFrame() {} @@ -501,6 +506,8 @@ protected: std::string reportingPrimaryInfo_; std::string reportingFullInfo_; + GPURecord::Recorder recorder_; + private: void DoExecuteCall(u32 target); void PopDLQueue(); diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 4616b34b8a..951c78c373 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 d0b0b8d035..e58d9adc12 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/UI/DevScreens.cpp b/UI/DevScreens.cpp index eeb7df2f14..fd0ea6e185 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/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 4bcf4de7e4..4b86803cec 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -1083,7 +1083,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { 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); });