mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
GE Debugger: Initial structure for GE data dumping.
This snapshots the GE commands and drawing to facilitate debugging.
This commit is contained in:
parent
41b485aa0a
commit
cbbd3cac7e
7 changed files with 256 additions and 1 deletions
|
@ -1264,6 +1264,8 @@ set(GPU_SOURCES
|
|||
GPU/Common/SplineCommon.h
|
||||
GPU/Debugger/Breakpoints.cpp
|
||||
GPU/Debugger/Breakpoints.h
|
||||
GPU/Debugger/Record.cpp
|
||||
GPU/Debugger/Record.h
|
||||
GPU/Debugger/Stepping.cpp
|
||||
GPU/Debugger/Stepping.h
|
||||
GPU/GPUInterface.h
|
||||
|
|
212
GPU/Debugger/Record.cpp
Normal file
212
GPU/Debugger/Record.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
// Copyright (c) 2017- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <vector>
|
||||
#include "base/stringutil.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/ELF/ParamSFO.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
||||
namespace GPURecord {
|
||||
|
||||
static const char *HEADER = "PPSSPPGE";
|
||||
static const int VERSION = 1;
|
||||
|
||||
static bool active = false;
|
||||
static bool nextFrame = false;
|
||||
|
||||
enum class CommandType : u8 {
|
||||
REGISTERS = 1,
|
||||
};
|
||||
|
||||
struct Command {
|
||||
CommandType type;
|
||||
std::vector<u8> buf;
|
||||
};
|
||||
|
||||
static std::vector<Command> commands;
|
||||
static std::vector<u32> lastRegisters;
|
||||
|
||||
static void FlushRegisters() {
|
||||
if (!lastRegisters.empty()) {
|
||||
Command last{CommandType::REGISTERS};
|
||||
last.buf.resize(lastRegisters.size() * sizeof(u32));
|
||||
memcpy(last.buf.data(), lastRegisters.data(), last.buf.size());
|
||||
lastRegisters.clear();
|
||||
|
||||
commands.push_back(last);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string GenRecordingFilename() {
|
||||
const std::string dumpDir = GetSysDirectory(DIRECTORY_DUMP);
|
||||
const std::string prefix = dumpDir + "/" + g_paramSFO.GetDiscID();
|
||||
|
||||
File::CreateFullPath(dumpDir);
|
||||
|
||||
for (int n = 1; n < 10000; ++n) {
|
||||
std::string filename = StringFromFormat("%s_%04d.ppdmp", prefix.c_str(), n);
|
||||
if (!File::Exists(filename)) {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
return StringFromFormat("%s_%04d.ppdmp", prefix.c_str(), 9999);
|
||||
}
|
||||
|
||||
static void WriteRecording() {
|
||||
FlushRegisters();
|
||||
|
||||
const std::string filename = GenRecordingFilename();
|
||||
FILE *fp = File::OpenCFile(filename, "wb");
|
||||
fwrite(HEADER, sizeof(HEADER), 1, fp);
|
||||
fwrite(&VERSION, sizeof(VERSION), 1, fp);
|
||||
|
||||
int sz = (int)commands.size();
|
||||
fwrite(&sz, sizeof(sz), 1, fp);
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
const Command &cmd = commands[i];
|
||||
fwrite(&cmd.type, sizeof(cmd.type), 1, fp);
|
||||
int bufsz = (int)cmd.buf.size();
|
||||
fwrite(&bufsz, sizeof(bufsz), 1, fp);
|
||||
fwrite(cmd.buf.data(), bufsz, 1, fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static void FlushPrimState() {
|
||||
// TODO: Eventually, how do we handle texturing from framebuf/zbuf?
|
||||
// TODO: Do we need to preload color/depth/stencil (in case from last frame)?
|
||||
|
||||
// TODO: Flush textures, always (in case overwritten... hmm expensive.)
|
||||
// TODO: Flush vertex data? Or part of prim?
|
||||
// TODO: Flush index data.
|
||||
}
|
||||
|
||||
static void EmitTransfer(u32 op) {
|
||||
FlushRegisters();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void EmitClut(u32 op) {
|
||||
FlushRegisters();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void EmitPrim(u32 op) {
|
||||
FlushPrimState();
|
||||
FlushRegisters();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void EmitBezier(u32 op) {
|
||||
FlushPrimState();
|
||||
FlushRegisters();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void EmitSpline(u32 op) {
|
||||
FlushPrimState();
|
||||
FlushRegisters();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool IsActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
void NotifyCommand(u32 pc) {
|
||||
const u32 op = Memory::Read_U32(pc);
|
||||
const GECommand cmd = GECommand(op >> 24);
|
||||
|
||||
switch (cmd) {
|
||||
case GE_CMD_VADDR:
|
||||
case GE_CMD_IADDR:
|
||||
case GE_CMD_JUMP:
|
||||
case GE_CMD_CALL:
|
||||
case GE_CMD_RET:
|
||||
case GE_CMD_END:
|
||||
case GE_CMD_SIGNAL:
|
||||
case GE_CMD_FINISH:
|
||||
case GE_CMD_BASE:
|
||||
case GE_CMD_OFFSETADDR:
|
||||
case GE_CMD_ORIGIN:
|
||||
case GE_CMD_CLUTADDR:
|
||||
case GE_CMD_CLUTADDRUPPER:
|
||||
case GE_CMD_TRANSFERSRC:
|
||||
case GE_CMD_TRANSFERSRCW:
|
||||
case GE_CMD_TRANSFERDST:
|
||||
case GE_CMD_TRANSFERDSTW:
|
||||
// These just prepare future commands, and are flushed with those commands.
|
||||
// TODO: Maybe add a command just to log that these were hit?
|
||||
break;
|
||||
|
||||
case GE_CMD_BOUNDINGBOX:
|
||||
case GE_CMD_BJUMP:
|
||||
// Since we record each command, this is theoretically not relevant.
|
||||
// TODO: Output a CommandType to validate this.
|
||||
break;
|
||||
|
||||
case GE_CMD_PRIM:
|
||||
EmitPrim(op);
|
||||
break;
|
||||
|
||||
case GE_CMD_BEZIER:
|
||||
EmitBezier(op);
|
||||
break;
|
||||
|
||||
case GE_CMD_SPLINE:
|
||||
EmitSpline(op);
|
||||
break;
|
||||
|
||||
case GE_CMD_LOADCLUT:
|
||||
EmitClut(op);
|
||||
break;
|
||||
|
||||
case GE_CMD_TRANSFERSTART:
|
||||
EmitTransfer(op);
|
||||
break;
|
||||
|
||||
default:
|
||||
lastRegisters.push_back(op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyFrame() {
|
||||
if (active) {
|
||||
active = false;
|
||||
WriteRecording();
|
||||
commands.clear();
|
||||
}
|
||||
if (nextFrame) {
|
||||
active = true;
|
||||
nextFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
29
GPU/Debugger/Record.h
Normal file
29
GPU/Debugger/Record.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2017- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace GPURecord {
|
||||
|
||||
bool IsActive();
|
||||
|
||||
void NotifyCommand(u32 pc);
|
||||
void NotifyFrame();
|
||||
|
||||
};
|
|
@ -218,6 +218,7 @@
|
|||
<ClInclude Include="D3D11\TextureScalerD3D11.h" />
|
||||
<ClInclude Include="D3D11\VertexShaderGeneratorD3D11.h" />
|
||||
<ClInclude Include="Debugger\Breakpoints.h" />
|
||||
<ClInclude Include="Debugger\Record.h" />
|
||||
<ClInclude Include="Debugger\Stepping.h" />
|
||||
<ClInclude Include="Directx9\DepalettizeShaderDX9.h" />
|
||||
<ClInclude Include="Directx9\GPU_DX9.h" />
|
||||
|
@ -318,6 +319,7 @@
|
|||
<ClCompile Include="D3D11\TextureScalerD3D11.cpp" />
|
||||
<ClCompile Include="D3D11\VertexShaderGeneratorD3D11.cpp" />
|
||||
<ClCompile Include="Debugger\Breakpoints.cpp" />
|
||||
<ClCompile Include="Debugger\Record.cpp" />
|
||||
<ClCompile Include="Debugger\Stepping.cpp" />
|
||||
<ClCompile Include="Directx9\DepalettizeShaderDX9.cpp" />
|
||||
<ClCompile Include="Directx9\GPU_DX9.cpp" />
|
||||
|
|
|
@ -262,6 +262,9 @@
|
|||
<ClInclude Include="Software\Sampler.h">
|
||||
<Filter>Software</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger\Record.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
|
@ -519,5 +522,8 @@
|
|||
<ClCompile Include="Software\SamplerX86.cpp">
|
||||
<Filter>Software</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\Record.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -25,6 +25,7 @@
|
|||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Common/TextureCacheCommon.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Debugger/Record.h"
|
||||
|
||||
const CommonCommandTableEntry commonCommandTable[] = {
|
||||
// From Common. No flushing but definitely need execute.
|
||||
|
@ -902,7 +903,7 @@ bool GPUCommon::InterpretList(DisplayList &list) {
|
|||
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
|
||||
guard.unlock();
|
||||
|
||||
const bool useDebugger = host->GPUDebuggingActive();
|
||||
const bool useDebugger = host->GPUDebuggingActive() || GPURecord::IsActive();
|
||||
const bool useFastRunLoop = !dumpThisFrame_ && !useDebugger;
|
||||
while (gpuState == GPUSTATE_RUNNING) {
|
||||
{
|
||||
|
@ -957,6 +958,7 @@ void GPUCommon::BeginFrameInternal() {
|
|||
} else if (dumpThisFrame_) {
|
||||
dumpThisFrame_ = false;
|
||||
}
|
||||
GPURecord::NotifyFrame();
|
||||
}
|
||||
|
||||
void GPUCommon::SlowRunLoop(DisplayList &list)
|
||||
|
@ -965,6 +967,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list)
|
|||
while (downcount > 0)
|
||||
{
|
||||
host->GPUNotifyCommand(list.pc);
|
||||
GPURecord::NotifyCommand(list.pc);
|
||||
u32 op = Memory::ReadUnchecked_U32(list.pc);
|
||||
u32 cmd = op >> 24;
|
||||
|
||||
|
|
|
@ -229,6 +229,7 @@ EXEC_AND_LIB_FILES := \
|
|||
$(SRC)/GPU/Common/PostShader.cpp \
|
||||
$(SRC)/GPU/Common/ShaderUniforms.cpp \
|
||||
$(SRC)/GPU/Debugger/Breakpoints.cpp \
|
||||
$(SRC)/GPU/Debugger/Record.cpp \
|
||||
$(SRC)/GPU/Debugger/Stepping.cpp \
|
||||
$(SRC)/GPU/GLES/FramebufferManagerGLES.cpp \
|
||||
$(SRC)/GPU/GLES/DepalettizeShaderGLES.cpp \
|
||||
|
|
Loading…
Add table
Reference in a new issue