GE Debugger: Initial structure for GE data dumping.

This snapshots the GE commands and drawing to facilitate debugging.
This commit is contained in:
Unknown W. Brackets 2017-06-02 21:53:20 -07:00
parent 41b485aa0a
commit cbbd3cac7e
7 changed files with 256 additions and 1 deletions

View file

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

View file

@ -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" />

View file

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

View file

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

View file

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