Refactor: Make GPUCommon own the framedump "recorder".

This commit is contained in:
Henrik Rydgård 2024-12-15 12:07:44 +01:00
parent 17e0680c12
commit 638607d29a
19 changed files with 173 additions and 121 deletions

View file

@ -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.

View file

@ -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;
});

View file

@ -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;
}

View file

@ -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 {

View file

@ -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 &params, u32 skipDrawReason);

View file

@ -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<int> &GetDisplayListQueue() = 0;
virtual const DisplayList &GetDisplayList(int index) = 0;
virtual GPURecord::Recorder *GetRecorder() = 0;
virtual bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
return false;
}

View file

@ -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);

View file

@ -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.

View file

@ -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);

View file

@ -48,35 +48,9 @@
namespace GPURecord {
static bool active = false;
static std::atomic<bool> nextFrame = false;
static int flipLastAction = -1;
static int flipFinishAt = -1;
static uint32_t lastEdramTrans = 0x400;
static std::function<void(const Path &)> writeCallback;
static std::vector<u8> pushbuf;
static std::vector<Command> commands;
static std::vector<u32> lastRegisters;
static std::vector<u32> lastTextures;
static std::set<u32> lastRenderTargets;
static std::vector<u8> 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<void(const Path &)> callback) {
bool Recorder::RecordNextFrame(const std::function<void(const Path &)> callback) {
if (!nextFrame) {
flipLastAction = gpuStats.numFlips;
flipFinishAt = -1;
@ -618,12 +584,7 @@ bool RecordNextFrame(const std::function<void(const Path &)> 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

View file

@ -18,24 +18,92 @@
#pragma once
#include <functional>
#include <atomic>
#include <vector>
#include <set>
#include "Common/CommonTypes.h"
#include "GPU/Debugger/RecordFormat.h"
class Path;
namespace GPURecord {
bool IsActive();
bool IsActivePending();
bool RecordNextFrame(const std::function<void(const Path &)> 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<void(const Path &)> 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<bool> nextFrame = false;
int flipLastAction = -1;
int flipFinishAt = -1;
uint32_t lastEdramTrans = 0x400;
std::function<void(const Path &)> writeCallback;
std::vector<u8> pushbuf;
std::vector<Command> commands;
std::vector<u32> lastRegisters;
std::vector<u32> lastTextures;
std::set<u32> lastRenderTargets;
std::vector<u8> lastVRAM;
DirtyVRAMFlag dirtyVRAM[DIRTY_VRAM_SIZE];
};
} // namespace GPURecord

View file

@ -186,7 +186,7 @@ bool ProcessStepping() {
return true;
}
bool EnterStepping() {
bool EnterStepping(CoreState coreState) {
_dbg_assert_(gpuDebug);
std::unique_lock<std::mutex> guard(pauseLock);
@ -216,7 +216,7 @@ bool EnterStepping() {
pauseAction = PAUSE_BREAK;
}
coreState = CORE_STEPPING_GE;
::coreState = CORE_STEPPING_GE;
return true;
}

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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_);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
});