diff --git a/CMakeLists.txt b/CMakeLists.txt index 98d4f79846..3396b64866 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp new file mode 100644 index 0000000000..75d56d4d1b --- /dev/null +++ b/GPU/Debugger/Record.cpp @@ -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 +#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 buf; +}; + +static std::vector commands; +static std::vector 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; + } +} + +}; diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h new file mode 100644 index 0000000000..3d5ea36b5c --- /dev/null +++ b/GPU/Debugger/Record.h @@ -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(); + +}; diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index dcf471a173..b53ade9739 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -218,6 +218,7 @@ + @@ -318,6 +319,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 2499cfab12..cbbd189fe9 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -262,6 +262,9 @@ Software + + Debugger + @@ -519,5 +522,8 @@ Software + + Debugger + \ No newline at end of file diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index c38a328abe..e409584d11 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -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; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 7b3dd28a06..501d2445cb 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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 \