Merge pull request #11564 from unknownbrackets/ge-dump

GE Debugger: Preserve VRAM textures / render-to-texture in dumps
This commit is contained in:
Henrik Rydgård 2018-11-17 23:32:20 +01:00 committed by GitHub
commit 036556c371
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 16 deletions

View File

@ -336,7 +336,7 @@ bool CreateFullPath(const std::string &path)
return true;
}
std::string subPath = fullPath.substr(0, position);
if (!File::Exists(subPath))
if (position != 0 && !File::Exists(subPath))
File::CreateDir(subPath);
// A safety check

View File

@ -1531,7 +1531,7 @@ skip:
}
// lw, sh, ...
if ((opInfo & IN_MEM) || (opInfo & OUT_MEM)) {
if (!IsSyscall(op) && (opInfo & (IN_MEM | OUT_MEM)) != 0) {
info.isDataAccess = true;
switch (opInfo & MEMTYPE_MASK) {
case MEMTYPE_BYTE:

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017- PPSSPP Project.
// 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
@ -18,6 +18,7 @@
#include <algorithm>
#include <cstring>
#include <functional>
#include <set>
#include <vector>
#include <snappy-c.h>
#include "base/stringutil.h"
@ -43,7 +44,11 @@
namespace GPURecord {
static const char *HEADER = "PPSSPPGE";
static const int VERSION = 2;
// Version 1: Uncompressed
// Version 2: Uses snappy
// Version 3: Adds FRAMEBUF0-FRAMEBUF9
static const int VERSION = 3;
static const int MIN_VERSION = 2;
static bool active = false;
static bool nextFrame = false;
@ -70,6 +75,15 @@ enum class CommandType : u8 {
TEXTURE5 = 0x15,
TEXTURE6 = 0x16,
TEXTURE7 = 0x17,
FRAMEBUF0 = 0x18,
FRAMEBUF1 = 0x19,
FRAMEBUF2 = 0x1A,
FRAMEBUF3 = 0x1B,
FRAMEBUF4 = 0x1C,
FRAMEBUF5 = 0x1D,
FRAMEBUF6 = 0x1E,
FRAMEBUF7 = 0x1F,
};
#pragma pack(push, 1)
@ -86,6 +100,7 @@ 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;
// TODO: Maybe move execute to another file?
class DumpExecute {
@ -109,6 +124,7 @@ private:
void MemcpyDest(u32 ptr, u32 sz);
void Memcpy(u32 ptr, u32 sz);
void Texture(int level, u32 ptr, u32 sz);
void Framebuf(int level, u32 ptr, u32 sz);
void Display(u32 ptr, u32 sz);
u32 execMemcpyDest = 0;
@ -358,7 +374,7 @@ static void FlushRegisters() {
static std::string GenRecordingFilename() {
const std::string dumpDir = GetSysDirectory(DIRECTORY_DUMP);
const std::string prefix = dumpDir + "/" + g_paramSFO.GetDiscID();
const std::string prefix = dumpDir + g_paramSFO.GetDiscID();
File::CreateFullPath(dumpDir);
@ -510,13 +526,36 @@ static void EmitTextureData(int level, u32 texaddr) {
int bufw = GetTextureBufw(level, texaddr, format);
int extraw = w > bufw ? w - bufw : 0;
u32 sizeInRAM = (textureBitsPerPixel[format] * (bufw * h + extraw)) / 8;
const bool isTarget = lastRenderTargets.find(texaddr) != lastRenderTargets.end();
CommandType type = CommandType((int)CommandType::TEXTURE0 + level);
const u8 *p = Memory::GetPointerUnchecked(texaddr);
u32 bytes = Memory::ValidSize(texaddr, sizeInRAM);
if (Memory::IsValidAddress(texaddr)) {
FlushRegisters();
std::vector<u8> framebufData;
CommandType type = CommandType((int)CommandType::TEXTURE0 + level);
const u8 *p = Memory::GetPointerUnchecked(texaddr);
if (Memory::IsVRAMAddress(texaddr)) {
struct FramebufData {
u32 addr;
int bufw;
u32 flags;
u32 pad;
};
// The isTarget flag is mostly used for replay of dumps on a PSP.
u32 flags = isTarget ? 1 : 0;
FramebufData framebuf{ texaddr, bufw, flags };
framebufData.resize(sizeof(framebuf) + bytes);
memcpy(&framebufData[0], &framebuf, sizeof(framebuf));
memcpy(&framebufData[sizeof(framebuf)], p, bytes);
p = &framebufData[0];
// Okay, now we'll just emit this instead.
type = CommandType((int)CommandType::FRAMEBUF0 + level);
bytes += (u32)sizeof(framebuf);
}
if (bytes > 0) {
FlushRegisters();
// Dumps are huge - let's try to find this already emitted.
for (u32 prevptr : lastTextures) {
@ -541,6 +580,9 @@ static void 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)?
lastRenderTargets.insert(PSP_GetVidMemBase() | gstate.getFrameBufRawAddress());
lastRenderTargets.insert(PSP_GetVidMemBase() | gstate.getDepthBufRawAddress());
// We re-flush textures always in case the game changed them... kinda expensive.
// TODO: Dirty textures on transfer/stall/etc. somehow?
// TODO: Or maybe de-dup by validating if it has changed?
@ -785,6 +827,7 @@ void NotifyFrame() {
active = true;
nextFrame = false;
lastTextures.clear();
lastRenderTargets.clear();
BeginRecording();
}
}
@ -980,6 +1023,33 @@ void DumpExecute::Texture(int level, u32 ptr, u32 sz) {
execListQueue.push_back((addrCmd << 24) | (psp & 0x00FFFFFF));
}
void DumpExecute::Framebuf(int level, u32 ptr, u32 sz) {
struct FramebufData {
u32 addr;
int bufw;
u32 flags;
u32 pad;
};
FramebufData *framebuf = (FramebufData *)(pushbuf.data() + ptr);
u32 bufwCmd = GE_CMD_TEXBUFWIDTH0 + level;
u32 addrCmd = GE_CMD_TEXADDR0 + level;
execListQueue.push_back((bufwCmd << 24) | ((framebuf->addr >> 8) & 0x00FF0000) | framebuf->bufw);
execListQueue.push_back((addrCmd << 24) | (framebuf->addr & 0x00FFFFFF));
lastBufw_[level] = framebuf->bufw;
// And now also copy the data into VRAM (in case it wasn't actually rendered.)
u32 headerSize = (u32)sizeof(FramebufData);
u32 pspSize = sz - headerSize;
const bool isTarget = (framebuf->flags & 1) != 0;
// Could potentially always skip if !isTarget, but playing it safe for offset texture behavior.
if (Memory::IsValidRange(framebuf->addr, pspSize) && (!isTarget || !g_Config.bSoftwareRendering)) {
// Intentionally don't trigger an upload here.
Memory::MemcpyUnchecked(framebuf->addr, pushbuf.data() + ptr + headerSize, pspSize);
}
}
void DumpExecute::Display(u32 ptr, u32 sz) {
struct DisplayBufData {
PSPPointer<u8> topaddr;
@ -1058,6 +1128,17 @@ bool DumpExecute::Run() {
Texture((int)cmd.type - (int)CommandType::TEXTURE0, cmd.ptr, cmd.sz);
break;
case CommandType::FRAMEBUF0:
case CommandType::FRAMEBUF1:
case CommandType::FRAMEBUF2:
case CommandType::FRAMEBUF3:
case CommandType::FRAMEBUF4:
case CommandType::FRAMEBUF5:
case CommandType::FRAMEBUF6:
case CommandType::FRAMEBUF7:
Framebuf((int)cmd.type - (int)CommandType::FRAMEBUF0, cmd.ptr, cmd.sz);
break;
case CommandType::DISPLAY:
Display(cmd.ptr, cmd.sz);
break;
@ -1100,7 +1181,7 @@ bool RunMountedReplay(const std::string &filename) {
pspFileSystem.ReadFile(fp, header, sizeof(header));
pspFileSystem.ReadFile(fp, (u8 *)&version, sizeof(version));
if (memcmp(header, HEADER, sizeof(header)) != 0 || version != VERSION) {
if (memcmp(header, HEADER, sizeof(header)) != 0 || version > VERSION || version < MIN_VERSION) {
ERROR_LOG(SYSTEM, "Invalid GE dump or unsupported version");
pspFileSystem.CloseFile(fp);
return false;

View File

@ -2615,6 +2615,7 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr);
u8 *dst = Memory::GetPointerUnchecked(dstLineStartAddr);
memcpy(dst, src, width * height * bpp);
GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, width * height * bpp);
} else {
for (int y = 0; y < height; y++) {
u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp;
@ -2623,6 +2624,7 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr);
u8 *dst = Memory::GetPointerUnchecked(dstLineStartAddr);
memcpy(dst, src, width * bpp);
GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, width * bpp);
}
}
@ -2724,24 +2726,24 @@ bool GPUCommon::PerformStencilUpload(u32 dest, int size) {
}
bool GPUCommon::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) {
u32 fb_address = type == GPU_DBG_FRAMEBUF_RENDER ? gstate.getFrameBufRawAddress() : framebufferManager_->DisplayFramebufAddr();
u32 fb_address = type == GPU_DBG_FRAMEBUF_RENDER ? (gstate.getFrameBufRawAddress() | 0x04000000) : framebufferManager_->DisplayFramebufAddr();
int fb_stride = type == GPU_DBG_FRAMEBUF_RENDER ? gstate.FrameBufStride() : framebufferManager_->DisplayFramebufStride();
GEBufferFormat format = type == GPU_DBG_FRAMEBUF_RENDER ? gstate.FrameBufFormat() : framebufferManager_->DisplayFramebufFormat();
return framebufferManager_->GetFramebuffer(fb_address, fb_stride, format, buffer, maxRes);
}
bool GPUCommon::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) {
u32 fb_address = gstate.getFrameBufRawAddress();
u32 fb_address = gstate.getFrameBufRawAddress() | 0x04000000;
int fb_stride = gstate.FrameBufStride();
u32 z_address = gstate.getDepthBufRawAddress();
u32 z_address = gstate.getDepthBufRawAddress() | 0x04000000;
int z_stride = gstate.DepthBufStride();
return framebufferManager_->GetDepthbuffer(fb_address, fb_stride, z_address, z_stride, buffer);
}
bool GPUCommon::GetCurrentStencilbuffer(GPUDebugBuffer &buffer) {
u32 fb_address = gstate.getFrameBufRawAddress();
u32 fb_address = gstate.getFrameBufRawAddress() | 0x04000000;
int fb_stride = gstate.FrameBufStride();
return framebufferManager_->GetStencilbuffer(fb_address, fb_stride, buffer);

View File

@ -204,7 +204,7 @@ static void ExpandBezier(int &count, int op, const std::vector<SimpleVertex> &si
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);
cpoints.Convert(points.data(), num_points);
surface.Init(generatedVerts.size());
surface.Init((int)generatedVerts.size());
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
count = output.count;
@ -256,7 +256,7 @@ static void ExpandSpline(int &count, int op, const std::vector<SimpleVertex> &si
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);
cpoints.Convert(points.data(), num_points);
surface.Init(generatedVerts.size());
surface.Init((int)generatedVerts.size());
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
count = output.count;