From 8506da14f07605c6846930d2be79439e89866e57 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 08:54:29 -0800 Subject: [PATCH 1/7] Debugger: Prevent invalid address on syscall. --- Core/MIPS/MIPSAnalyst.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 43e5019a66..7a3459f808 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -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: From 2201c65b000a4e8b03d09816d4e8a2803f421e33 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 08:55:00 -0800 Subject: [PATCH 2/7] Core: Fix spurious error on Linux/Unix paths. CreateFullPath with an absolute path would try to create ''. --- Common/FileUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index c6a19a2c00..e8dbc62091 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -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 From d245e002c72fd05f16c9aa3159c777c8da49ea66 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 08:55:54 -0800 Subject: [PATCH 3/7] GE Debugger: Record block transfer too. --- GPU/GPUCommon.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1e58cb2a6e..39b2e7263d 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -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); } } From 6cba46bd62a2a19ba919c35d06b27ee4c7e436dd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 08:56:32 -0800 Subject: [PATCH 4/7] GE Debugger: Fix handling for framebuf changes. --- GPU/GPUCommon.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 39b2e7263d..ecc9163f3f 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2726,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); From 184ae2f93c672331ebff08e17ec65c13ed7e3ec8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 08:57:59 -0800 Subject: [PATCH 5/7] GE Debugger: Fix type conversion warning. --- Windows/GEDebugger/VertexPreview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index 79183d236f..b608754b66 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -204,7 +204,7 @@ static void ExpandBezier(int &count, int op, const std::vector &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 &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; From 169d2fa44410391ced08ae9cafb2bcb9fbaafceb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 08:59:00 -0800 Subject: [PATCH 6/7] GE Debugger: Preserve VRAM textures in dumps. Should allow for (some) reproduction of render-to-texture behavior. --- GPU/Debugger/Record.cpp | 96 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 8 deletions(-) diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 1fa237a600..6f42ff8f21 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -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 #include #include +#include #include #include #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 pushbuf; static std::vector commands; static std::vector lastRegisters; static std::vector lastTextures; +static std::set 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); @@ -528,13 +544,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 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) { @@ -559,6 +598,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? @@ -782,6 +824,7 @@ void NotifyFrame() { active = true; nextFrame = false; lastTextures.clear(); + lastRenderTargets.clear(); BeginRecording(); } } @@ -977,6 +1020,32 @@ 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; + // TODO: Potentially skip this if flags & 1 (means it was rendered to previously.) + if (Memory::IsValidRange(framebuf->addr, pspSize)) { + // 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 topaddr; @@ -1055,6 +1124,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; @@ -1097,7 +1177,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; From 6383f407d18cce6870bd69660f9ef9597b5cdadc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2018 09:06:02 -0800 Subject: [PATCH 7/7] GE Debugger: Avoid overwriting in softgpu playback. --- GPU/Debugger/Record.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 6f42ff8f21..6075761c0a 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -1039,8 +1039,9 @@ void DumpExecute::Framebuf(int level, u32 ptr, u32 sz) { // And now also copy the data into VRAM (in case it wasn't actually rendered.) u32 headerSize = (u32)sizeof(FramebufData); u32 pspSize = sz - headerSize; - // TODO: Potentially skip this if flags & 1 (means it was rendered to previously.) - if (Memory::IsValidRange(framebuf->addr, pspSize)) { + 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); }