From 5bdb9e976b97f4040b76cf7b0a219d921b4340cf Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 21 Sep 2013 18:52:30 +0200 Subject: [PATCH] Track copies of framebuffers in ram created through sceDmacMemcpy, so that we can display them. Fixes MotoGP while also, in effect, committing #3859. Removes the horrifying ramDisplayFramebufferPtr hack. --- Core/HLE/sceDmac.cpp | 16 ++++----- Core/HLE/sceKernelInterrupt.cpp | 4 +++ Core/MemMap.h | 2 ++ Core/MemMapFunctions.cpp | 17 ++++++++-- GPU/Directx9/FramebufferDX9.cpp | 1 - GPU/GLES/Framebuffer.cpp | 58 +++++++++++++++++++-------------- GPU/GLES/Framebuffer.h | 10 ++++-- GPU/GLES/GLES_GPU.cpp | 9 +++-- 8 files changed, 77 insertions(+), 40 deletions(-) diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 74d6abd221..8e11814dae 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -16,15 +16,14 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Globals.h" +#include "Core/MemMap.h" #include "Core/Reporting.h" #include "Core/HLE/HLE.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" -u32 sceDmacMemcpy(u32 dst, u32 src, u32 size) -{ - if (!Memory::IsValidAddress(dst) || !Memory::IsValidAddress(src)) - { +u32 sceDmacMemcpy(u32 dst, u32 src, u32 size) { + if (!Memory::IsValidAddress(dst) || !Memory::IsValidAddress(src)) { ERROR_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): invalid address", dst, src, size); return 0; } @@ -35,18 +34,17 @@ u32 sceDmacMemcpy(u32 dst, u32 src, u32 size) src &= ~0x40000000; dst &= ~0x40000000; - if ((src >= PSP_GetVidMemBase() && src < PSP_GetVidMemEnd()) || (dst >= PSP_GetVidMemBase() && dst < PSP_GetVidMemEnd())) + if (Memory::IsVRAMAddress(src) || Memory::IsVRAMAddress(dst)) { gpu->UpdateMemory(dst, src, size); + } return 0; } -const HLEFunction sceDmac[] = -{ +const HLEFunction sceDmac[] = { {0x617f3fe6, &WrapU_UUU, "sceDmacMemcpy"}, {0xd97f94d8, 0, "sceDmacTryMemcpy"}, }; -void Register_sceDmac() -{ +void Register_sceDmac() { RegisterModule("sceDmac", ARRAY_SIZE(sceDmac), sceDmac); } diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 48c7e06ee9..0ffda2646e 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -28,6 +28,7 @@ #include "sceKernelInterrupt.h" #include "sceKernelMemory.h" #include "sceKernelMutex.h" +#include "GPU/GPUCommon.h" void __DisableInterrupts(); void __EnableInterrupts(); @@ -551,6 +552,9 @@ u32 sceKernelMemset(u32 addr, u32 fillc, u32 n) u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) { DEBUG_LOG(SCEKERNEL, "sceKernelMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size); + // Hm, sceDmacMemcpy seems to be the popular one for this. Ignoring for now. + // gpu->UpdateMemory(dst, src, size); + // Technically should crash if these are invalid and size > 0... if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src) && Memory::IsValidAddress(dst + size - 1) && Memory::IsValidAddress(src + size - 1)) { diff --git a/Core/MemMap.h b/Core/MemMap.h index 157943bc95..ee0546b158 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -243,6 +243,8 @@ inline void Write_Float(float f, u32 address) void GetString(std::string& _string, const u32 _Address); u8* GetPointer(const u32 address); bool IsValidAddress(const u32 address); +bool IsRAMAddress(const u32 address); +bool IsVRAMAddress(const u32 address); inline const char* GetCharPointer(const u32 address) { return (const char *)GetPointer(address); diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index 77a1bca850..c8f1ec7089 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -145,8 +145,7 @@ inline void WriteToHardware(u32 address, const T data) // ===================== -bool IsValidAddress(const u32 address) -{ +bool IsValidAddress(const u32 address) { if ((address & 0x3E000000) == 0x08000000) { return true; } @@ -163,6 +162,20 @@ bool IsValidAddress(const u32 address) return false; } +bool IsRAMAddress(const u32 address) { + if ((address & 0x3E000000) == 0x08000000) { + return true; + } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { + return true; + } else { + return false; + } +} + +bool IsVRAMAddress(const u32 address) { + return ((address & 0x3F800000) == 0x04000000); +} + u8 Read_U8(const u32 _Address) { u8 _var = 0; diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 6f56b4d41f..5fa7c7a82d 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -113,7 +113,6 @@ static void DisableState() { FramebufferManagerDX9::FramebufferManagerDX9() : -ramDisplayFramebufPtr_(0), displayFramebufPtr_(0), displayStride_(0), displayFormat_(GE_FORMAT_565), diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 0f988cb014..3436138413 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -165,7 +165,6 @@ void FramebufferManager::CompileDraw2DProgram() { } FramebufferManager::FramebufferManager() : - ramDisplayFramebufPtr_(0), displayFramebufPtr_(0), displayStride_(0), displayFormat_(GE_FORMAT_565), @@ -369,11 +368,11 @@ void FramebufferManager::DrawActiveTexture(float x, float y, float w, float h, b glsl_unbind(); } -VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { +VirtualFramebuffer *FramebufferManager::GetVFBAt(u32 addr) { VirtualFramebuffer *match = NULL; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *v = vfbs_[i]; - if (MaskedEqual(v->fb_address, displayFramebufPtr_) && v->format == displayFormat_ && v->width >= 480) { + if (MaskedEqual(v->fb_address, addr) && v->format == displayFormat_ && v->width >= 480) { // Could check w too but whatever if (match == NULL || match->last_frame_render < v->last_frame_render) { match = v; @@ -384,7 +383,7 @@ VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { return match; } - DEBUG_LOG(SCEGE, "Finding no FBO matching address %08x", displayFramebufPtr_); + DEBUG_LOG(SCEGE, "Finding no FBO matching address %08x", addr); #if 0 // defined(_DEBUG) std::string debug = "FBOs: "; for (size_t i = 0; i < vfbs_.size(); ++i) { @@ -687,20 +686,29 @@ void FramebufferManager::CopyDisplayToOutput() { fbo_unbind(); currentRenderVfb_ = 0; - VirtualFramebuffer *vfb = GetDisplayFBO(); + VirtualFramebuffer *vfb = GetVFBAt(displayFramebufPtr_); if (!vfb) { - if (Memory::IsValidAddress(ramDisplayFramebufPtr_)) { + if (Memory::IsValidAddress(displayFramebufPtr_)) { // The game is displaying something directly from RAM. In GTA, it's decoded video. - DrawPixels(Memory::GetPointer(ramDisplayFramebufPtr_), displayFormat_, displayStride_); - } else if (Memory::IsValidAddress(displayFramebufPtr_)) { - // The game is displaying something directly from RAM. In GTA, it's decoded video. - DrawPixels(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_); + + // First check that it's not a known RAM copy of a VRAM framebuffer though, as in MotoGP + for (auto iter = knownFramebufferCopies_.begin(); iter != knownFramebufferCopies_.end(); ++iter) { + if (iter->second == displayFramebufPtr_) { + vfb = GetVFBAt(iter->first); + } + } + + if (!vfb) { + // Just a pointer to plain memory to draw. Draw it. + DrawPixels(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_); + return; + } } else { DEBUG_LOG(SCEGE, "Found no FBO to display! displayFBPtr = %08x", displayFramebufPtr_); // No framebuffer to display! Clear to black. ClearBuffer(); + return; } - return; } vfb->usageFlags |= FB_USAGE_DISPLAYED_FRAMEBUFFER; @@ -1191,18 +1199,9 @@ void FramebufferManager::BeginFrame() { } void FramebufferManager::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { - - if ((framebuf & 0x04000000) == 0) { - DEBUG_LOG(SCEGE, "Non-VRAM display framebuffer address set: %08x", framebuf); - ramDisplayFramebufPtr_ = framebuf; - displayStride_ = stride; - displayFormat_ = format; - } else { - ramDisplayFramebufPtr_ = 0; - displayFramebufPtr_ = framebuf; - displayStride_ = stride; - displayFormat_ = format; - } + displayFramebufPtr_ = framebuf; + displayStride_ = stride; + displayFormat_ = format; } std::vector FramebufferManager::GetFramebufferList() { @@ -1224,6 +1223,17 @@ std::vector FramebufferManager::GetFramebufferList() { return list; } +// MotoGP workaround +void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dest, int size) { + for (size_t i = 0; i < vfbs_.size(); i++) { + // This size fits for MotoGP. Might want to make this more flexible for other games if they do the same. + if ((vfbs_[i]->fb_address | 0x04000000) == src && size == 512 * 272 * 2) { + // A framebuffer matched! + knownFramebufferCopies_.insert(std::pair(src, dest)); + } + } +} + void FramebufferManager::DecimateFBOs() { fbo_unbind(); currentRenderVfb_ = 0; @@ -1237,7 +1247,7 @@ void FramebufferManager::DecimateFBOs() { int age = frameLastFramebufUsed - std::max(vfb->last_frame_render, vfb->last_frame_used); if(useMem && age == 0 && !vfb->memoryUpdated) { - ReadFramebufferToMemory(vfb); + ReadFramebufferToMemory(vfb); } if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) { diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index a8649d81ed..321ec746fe 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -145,7 +145,10 @@ public: #endif // TODO: Break out into some form of FBO manager - VirtualFramebuffer *GetDisplayFBO(); + VirtualFramebuffer *GetVFBAt(u32 addr); + VirtualFramebuffer *GetDisplayVFB() { + return GetVFBAt(displayFramebufPtr_); + } void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); size_t NumVFBs() const { return vfbs_.size(); } @@ -163,12 +166,13 @@ public: return displayFramebuf_ ? (0x04000000 | displayFramebuf_->fb_address) : 0; } + void NotifyFramebufferCopy(u32 src, u32 dest, int size); + void DestroyFramebuf(VirtualFramebuffer *vfb); private: void CompileDraw2DProgram(); - u32 ramDisplayFramebufPtr_; // workaround for MotoGP insanity u32 displayFramebufPtr_; u32 displayStride_; GEBufferFormat displayFormat_; @@ -191,6 +195,8 @@ private: int gpuVendor; std::vector bvfbs_; // blitting FBOs + std::set> knownFramebufferCopies_; + #ifndef USING_GLES2 AsyncPBO *pixelBufObj_; //this isn't that large u8 currentPBO_; diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 9aff8d7170..c30fed6202 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -529,7 +529,7 @@ bool GLES_GPU::FramebufferDirty() { SyncThread(); } - VirtualFramebuffer *vfb = framebufferManager_.GetDisplayFBO(); + VirtualFramebuffer *vfb = framebufferManager_.GetDisplayVFB(); if (vfb) { bool dirty = vfb->dirtyAfterDisplay; vfb->dirtyAfterDisplay = false; @@ -547,7 +547,7 @@ bool GLES_GPU::FramebufferReallyDirty() { SyncThread(); } - VirtualFramebuffer *vfb = framebufferManager_.GetDisplayFBO(); + VirtualFramebuffer *vfb = framebufferManager_.GetDisplayVFB(); if (vfb) { bool dirty = vfb->reallyDirtyAfterDisplay; vfb->reallyDirtyAfterDisplay = false; @@ -1450,6 +1450,11 @@ void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType t void GLES_GPU::UpdateMemory(u32 dest, u32 src, int size) { InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + + // Track stray copies of a framebuffer in RAM. MotoGP does this. + if (Memory::IsVRAMAddress(src) && Memory::IsRAMAddress(dest)) { + framebufferManager_.NotifyFramebufferCopy(src, dest, size); + } } void GLES_GPU::ClearCacheNextFrame() {