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 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: diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 70572a2efd..c875787337 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); @@ -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 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 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; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1e58cb2a6e..ecc9163f3f 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); } } @@ -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); 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;