mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Refactor: Make GPUCommon own the framedump "recorder".
This commit is contained in:
parent
17e0680c12
commit
638607d29a
19 changed files with 173 additions and 121 deletions
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue