From 30222f987dd64553e68ec1b0255471084ff0e2af Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 15:48:28 -0700 Subject: [PATCH 01/22] Flip framebuffers when downloading their pixels. It seems like this is where it was being done... --- GPU/GLES/Framebuffer.cpp | 8 ++++++-- GPU/GLES/Framebuffer.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 51b6aa2f86..85a27befeb 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1231,7 +1231,7 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s } vfb->memoryUpdated = true; - BlitFramebuffer_(nvfb, x, y, vfb, x, y, w, h, 0); + BlitFramebuffer_(nvfb, x, y, vfb, x, y, w, h, 0, true); // PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels // PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs @@ -1254,7 +1254,7 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s } // TODO: If dimensions are the same, we can use glCopyImageSubData. -void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) { +void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, bool flip) { if (!dst->fbo) { ERROR_LOG_REPORT_ONCE(dstfbozero, SCEGE, "BlitFramebuffer_: dst->fbo == 0"); fbo_unbind(); @@ -1300,6 +1300,10 @@ void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int int dstY2 = dst->renderHeight - (h + dstY) * dstYFactor; int dstY1 = dstY2 + h * dstYFactor; + if (flip) { + std::swap(dstY1, dstY2); + } + #ifdef MAY_HAVE_GLES3 fbo_bind_for_read(src->fbo); if (!useNV) { diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index a3ebf726c8..0744271f4b 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -227,7 +227,7 @@ private: VirtualFramebuffer *currentRenderVfb_; // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp); + void BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, bool flip = false); #ifndef USING_GLES2 void PackFramebufferAsync_(VirtualFramebuffer *vfb); #endif From 0e620b2fe95f9029a07027b3bed709322698971a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 16:28:13 -0700 Subject: [PATCH 02/22] Fix buffer overflow in MakePixelTexture(). --- GPU/GLES/Framebuffer.cpp | 21 ++++++++++++--------- GPU/GLES/Framebuffer.h | 3 ++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 85a27befeb..f505ea7d45 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -318,7 +318,7 @@ FramebufferManager::FramebufferManager() : currentRenderVfb_(0), drawPixelsTex_(0), drawPixelsTexFormat_(GE_FORMAT_INVALID), - convBuf(0), + convBuf_(0), draw2dprogram_(0), postShaderProgram_(0), plainColorLoc_(-1), @@ -361,7 +361,7 @@ FramebufferManager::~FramebufferManager() { #ifndef USING_GLES2 delete [] pixelBufObj_; #endif - delete [] convBuf; + delete [] convBuf_; } void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) { @@ -394,15 +394,18 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr bool useConvBuf = false; if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) { useConvBuf = true; - if (!convBuf) { - convBuf = new u8[width * height * 4]; + u32 neededSize = width * height * 4; + if (!convBuf_ || convBufSize_ < neededSize) { + delete [] convBuf_; + convBuf_ = new u8[neededSize]; + convBufSize_ = neededSize; } for (int y = 0; y < height; y++) { switch (srcPixelFormat) { case GE_FORMAT_565: { const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf + 4 * width * y; + u8 *dst = convBuf_ + 4 * width * y; for (int x = 0; x < width; x++) { u16 col = src[x]; @@ -417,7 +420,7 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr case GE_FORMAT_5551: { const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf + 4 * width * y; + u8 *dst = convBuf_ + 4 * width * y; for (int x = 0; x < width; x++) { u16 col = src[x]; @@ -432,7 +435,7 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr case GE_FORMAT_4444: { const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf + 4 * width * y; + u8 *dst = convBuf_ + 4 * width * y; for (int x = 0; x < width; x++) { u16 col = src[x]; @@ -447,7 +450,7 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr case GE_FORMAT_8888: { const u8 *src = srcPixels + srcStride * 4 * y; - u8 *dst = convBuf + 4 * width * y; + u8 *dst = convBuf_ + 4 * width * y; memcpy(dst, src, 4 * width); } break; @@ -458,7 +461,7 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr } } } - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf : srcPixels); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf_ : srcPixels); } void FramebufferManager::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) { diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 0744271f4b..a7d67b258b 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -239,7 +239,8 @@ private: int drawPixelsTexW_; int drawPixelsTexH_; - u8 *convBuf; + u8 *convBuf_; + u32 convBufSize_; GLSLProgram *draw2dprogram_; GLSLProgram *plainColorProgram_; GLSLProgram *postShaderProgram_; From 4dbb4328aeb32b86f69af2345abfc9ba7188128a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 16:00:39 -0700 Subject: [PATCH 03/22] Download before, upload after. Fix save pictures. Otherwise, it only works if it does it every frame, and then it's a frame behind. Also fixes Tales of Phantasia X's fades and etc. --- GPU/GLES/Framebuffer.cpp | 95 +++++++++++++++++++++++----------------- GPU/GLES/Framebuffer.h | 5 ++- GPU/GLES/GLES_GPU.cpp | 39 +++++++++-------- 3 files changed, 80 insertions(+), 59 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index f505ea7d45..7f6980e982 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1846,11 +1846,63 @@ void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { } } -bool FramebufferManager::NotifyBlockTransfer(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp) { +void FramebufferManager::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int bpp) const { + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + const u32 vfb_address = 0x04000000 | vfb->fb_address; + const u32 vfb_size = vfb->fb_stride * vfb->height * (vfb->format == GE_FORMAT_8888 ? 4 : 2); + if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) { + dstY += (dstBasePtr - vfb_address) / (dstStride * bpp); + dstBuffer = vfb; + } + if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { + srcY += (srcBasePtr - vfb_address) / (srcStride * bpp); + srcBuffer = vfb; + } + } +} + +bool FramebufferManager::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp) { if (!(g_Config.iRenderingMode == FB_BUFFERED_MODE)) { return false; } - + + VirtualFramebuffer *dstBuffer = 0; + VirtualFramebuffer *srcBuffer = 0; + FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, bpp); + + if (dstBuffer && srcBuffer) { + if (srcBuffer == dstBuffer) { + WARN_LOG_REPORT_ONCE(dstsrc, G3D, "Intra-buffer block transfer (not supported) %08x -> %08x", srcBasePtr, dstBasePtr); + } else { + WARN_LOG_ONCE(dstnotsrc, G3D, "Inter-buffer block transfer %08x -> %08x", srcBasePtr, dstBasePtr); + // Just do the blit! + // TODO: Possibly take bpp into account somehow if games are doing really crazy things? + if (g_Config.bBlockTransferGPU) { + BlitFramebuffer_(dstBuffer, dstX, dstY, srcBuffer, srcX, srcY, width, height, bpp); + return true; // No need to actually do the memory copy behind, probably. + } + } + return false; + } else if (dstBuffer) { + // Here we should just draw the pixels into the buffer. Copy first. + return false; + } else if (srcBuffer) { + WARN_LOG_ONCE(btd, G3D, "Block transfer download %08x -> %08x", srcBasePtr, dstBasePtr); + if (g_Config.bBlockTransferGPU) { + ReadFramebufferToMemory(srcBuffer, true, srcX, srcY, width, height); + } + return false; // Let the bit copy happen + } else { + return false; + } +} + +void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp) { + if (!(g_Config.iRenderingMode == FB_BUFFERED_MODE)) { + return; + } + if (Memory::IsRAMAddress(srcBasePtr) && Memory::IsVRAMAddress(dstBasePtr)) { // TODO: This causes glitches in Tactics Ogre if we don't implement both ways (which will probably be slow...) // The main thing this helps is videos, which will have a matching stride, and zero x/y. @@ -1874,49 +1926,14 @@ bool FramebufferManager::NotifyBlockTransfer(u32 dstBasePtr, int dstStride, int VirtualFramebuffer *dstBuffer = 0; VirtualFramebuffer *srcBuffer = 0; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - const u32 vfb_address = 0x04000000 | vfb->fb_address; - const u32 vfb_size = vfb->fb_stride * vfb->height * (vfb->format == GE_FORMAT_8888 ? 4 : 2); - if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) { - dstY += (dstBasePtr - vfb_address) / (dstStride * bpp); - dstBuffer = vfb; - } - if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { - srcY += (srcBasePtr - vfb_address) / (srcStride * bpp); - srcBuffer = vfb; - } - } + FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, bpp); - if (dstBuffer && srcBuffer) { - if (srcBuffer == dstBuffer) { - WARN_LOG_REPORT_ONCE(dstsrc, G3D, "Intra-buffer block transfer (not supported) %08x -> %08x", srcBasePtr, dstBasePtr); - } else { - WARN_LOG_ONCE(dstnotsrc, G3D, "Inter-buffer block transfer %08x -> %08x", srcBasePtr, dstBasePtr); - // Just do the blit! - // TODO: Possibly take bpp into account somehow if games are doing really crazy things? - if (g_Config.bBlockTransferGPU) { - BlitFramebuffer_(dstBuffer, dstX, dstY, srcBuffer, srcX, srcY, width, height, bpp); - return true; // No need to actually do the memory copy behind, probably. - } - } - return false; - } else if (dstBuffer) { + if (dstBuffer && !srcBuffer) { WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload (not supported) %08x -> %08x", srcBasePtr, dstBasePtr); if (g_Config.bBlockTransferGPU) { u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride * bpp, width, height); } - // Here we should just draw the pixels into the buffer. - return false; - } else if (srcBuffer && g_Config.iRenderingMode == FB_BUFFERED_MODE) { - WARN_LOG_ONCE(btd, G3D, "Block transfer download %08x -> %08x", srcBasePtr, dstBasePtr); - if (g_Config.bBlockTransferGPU) { - ReadFramebufferToMemory(srcBuffer, true, srcX, srcY, width, height); - } - return false; // Let the bit copy happen - } else { - return false; } } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index a7d67b258b..1d58a118cc 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -166,7 +166,8 @@ public: // Returns true if it's sure this is a direct FBO->FBO transfer and it has already handle it. // In that case we hardly need to actually copy the bytes in VRAM, they will be wrong anyway (unless // read framebuffers is on, in which case this should always return false). - bool NotifyBlockTransfer(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp); + bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp); + void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp); // Reads a rectangular subregion of a framebuffer to the right position in its backing memory. void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h); @@ -211,6 +212,8 @@ private: void CompileDraw2DProgram(); void DestroyDraw2DProgram(); + void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int bpp) const; + void SetNumExtraFBOs(int num); u32 displayFramebufPtr_; diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 679eb5496e..bdb4697fd4 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1908,30 +1908,31 @@ void GLES_GPU::DoBlockTransfer() { return; } - // Do the copy! (Hm, if we detect a drawn video frame (see below) then we could maybe skip this?) - // Can use GetPointerUnchecked because we checked the addresses above. We could also avoid them - // entirely by walking a couple of pointers... - if (srcStride == dstStride && width == srcStride) { - // Common case in God of War, let's do it all in one chunk. - u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp; - u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr); - u8 *dst = Memory::GetPointerUnchecked(dstLineStartAddr); - memcpy(dst, src, width * height * bpp); - } else { - for (int y = 0; y < height; y++) { - u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; - u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; - + // Tell the framebuffer manager to take action if possible. If it does the entire thing, let's just return. + if (!framebufferManager_.NotifyBlockTransferBefore(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp)) { + // Do the copy! (Hm, if we detect a drawn video frame (see below) then we could maybe skip this?) + // Can use GetPointerUnchecked because we checked the addresses above. We could also avoid them + // entirely by walking a couple of pointers... + if (srcStride == dstStride && width == srcStride) { + // Common case in God of War, let's do it all in one chunk. + u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp; + u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp; const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr); u8 *dst = Memory::GetPointerUnchecked(dstLineStartAddr); - memcpy(dst, src, width * bpp); + memcpy(dst, src, width * height * bpp); + } else { + for (int y = 0; y < height; y++) { + u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; + u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; + + const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr); + u8 *dst = Memory::GetPointerUnchecked(dstLineStartAddr); + memcpy(dst, src, width * bpp); + } } - } - // Tell the framebuffer manager to take action if possible. If it does the entire thing, let's just return. - if (!framebufferManager_.NotifyBlockTransfer(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp)) { textureCache_.Invalidate(dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, GPU_INVALIDATE_HINT); + framebufferManager_.NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp); } #ifndef MOBILE_DEVICE From fd4ba0093c669d6efa00909094d3a239e1392c50 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:01:28 -0700 Subject: [PATCH 04/22] Skip some code if things are outside framebuffers. Small optimization. --- GPU/GLES/Framebuffer.cpp | 40 ++++++++++++++++++++++++++++++---------- GPU/GLES/Framebuffer.h | 15 ++++++++++++++- GPU/GLES/GLES_GPU.cpp | 3 ++- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 7f6980e982..a99b02c6b9 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -327,7 +327,8 @@ FramebufferManager::FramebufferManager() : shaderManager_(0), usePostShader_(false), postShaderAtOutputResolution_(false), - resized_(false) + resized_(false), + framebufRangeEnd_(0) #ifndef USING_GLES2 , pixelBufObj_(0), @@ -836,6 +837,11 @@ void FramebufferManager::DoSetRenderFrameBuffer() { glEnable(GL_DITHER); // why? currentRenderVfb_ = vfb; + u32 byteSize = vfb->fb_stride * vfb->height * (vfb->format == GE_FORMAT_8888 ? 4 : 2); + if (fb_address + byteSize > framebufRangeEnd_) { + framebufRangeEnd_ = ((fb_address + byteSize) & 0x3FFFFFFF) | 0x04000000; + } + INFO_LOG(SCEGE, "Creating FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); // Let's check for depth buffer overlap. Might be interesting. @@ -1798,6 +1804,11 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size, bool safe) { } void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { + if (!MayIntersectFramebuffer(src) && !MayIntersectFramebuffer(dst)) { + // Don't waste time looking if neither can be a framebuffer. + return; + } + // MotoGP workaround for (size_t i = 0; i < vfbs_.size(); i++) { int bpp = vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2; @@ -1867,6 +1878,11 @@ bool FramebufferManager::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride return false; } + // Skip checking if there's no framebuffers in that area. + if (!MayIntersectFramebuffer(srcBasePtr) && !MayIntersectFramebuffer(dstBasePtr)) { + return false; + } + VirtualFramebuffer *dstBuffer = 0; VirtualFramebuffer *srcBuffer = 0; FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, bpp); @@ -1903,7 +1919,8 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, return; } - if (Memory::IsRAMAddress(srcBasePtr) && Memory::IsVRAMAddress(dstBasePtr)) { + // TODO: This can probably just be handled by a normal block transfer upload, no? + if (Memory::IsRAMAddress(srcBasePtr) && MayIntersectFramebuffer(dstBasePtr)) { // TODO: This causes glitches in Tactics Ogre if we don't implement both ways (which will probably be slow...) // The main thing this helps is videos, which will have a matching stride, and zero x/y. if (dstStride == srcStride && dstY == 0 && dstX == 0 && srcX == 0 && srcY == 0) { @@ -1917,6 +1934,7 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, u32 backBuffer = PrevDisplayFramebufAddr(); u32 displayBuffer = DisplayFramebufAddr(); + // TODO: Is this not handled by upload? Should we check !dstBuffer to avoid a double copy? if (((backBuffer != 0 && dstBasePtr == backBuffer) || (displayBuffer != 0 && dstBasePtr == displayBuffer)) && dstStride == 512 && height == 272) { @@ -1924,15 +1942,17 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, DrawFramebuffer(Memory::GetPointerUnchecked(dstBasePtr), GE_FORMAT_8888, 512, false); } - VirtualFramebuffer *dstBuffer = 0; - VirtualFramebuffer *srcBuffer = 0; - FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, bpp); + if (MayIntersectFramebuffer(srcBasePtr) || MayIntersectFramebuffer(dstBasePtr)) { + VirtualFramebuffer *dstBuffer = 0; + VirtualFramebuffer *srcBuffer = 0; + FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, bpp); - if (dstBuffer && !srcBuffer) { - WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload (not supported) %08x -> %08x", srcBasePtr, dstBasePtr); - if (g_Config.bBlockTransferGPU) { - u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; - DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride * bpp, width, height); + if (dstBuffer && !srcBuffer) { + WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload (not supported) %08x -> %08x", srcBasePtr, dstBasePtr); + if (g_Config.bBlockTransferGPU) { + u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; + DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride * bpp, width, height); + } } } } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 1d58a118cc..c2ed32c8b5 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -200,6 +200,16 @@ public: } } + bool MayIntersectFramebuffer(u32 start) { + // Clear the cache/kernel bits. + start = start & 0x3FFFFFFF; + // Most games only have two framebuffers at the start. + if (start >= framebufRangeEnd_ || start < PSP_GetVidMemBase()) { + return false; + } + return true; + } + void NotifyFramebufferCopy(u32 src, u32 dest, int size); void DestroyFramebuf(VirtualFramebuffer *vfb); @@ -261,7 +271,10 @@ private: bool resized_; bool useBufferedRendering_; bool updateVRAM_; - + + // The range of PSP memory that may contain FBOs. So we can skip iterating. + u32 framebufRangeEnd_; + std::vector bvfbs_; // blitting FBOs std::map, FBO *> renderCopies_; diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index bdb4697fd4..f90a01deb8 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1955,8 +1955,9 @@ void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType t else textureCache_.InvalidateAll(type); - if (type != GPU_INVALIDATE_ALL) + if (type != GPU_INVALIDATE_ALL && framebufferManager_.MayIntersectFramebuffer(addr)) { framebufferManager_.UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE); + } } void GLES_GPU::UpdateMemory(u32 dest, u32 src, int size) { From 976968c70768b1e513b3f1e63612263e109fe074 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:03:32 -0700 Subject: [PATCH 05/22] Track more kinds of memcpy operations on fbos. --- GPU/GLES/Framebuffer.cpp | 19 ++++++++----------- GPU/GLES/GLES_GPU.cpp | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index a99b02c6b9..67e208bc00 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1804,18 +1804,15 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size, bool safe) { } void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { - if (!MayIntersectFramebuffer(src) && !MayIntersectFramebuffer(dst)) { - // Don't waste time looking if neither can be a framebuffer. - return; - } - // MotoGP workaround - for (size_t i = 0; i < vfbs_.size(); i++) { - int bpp = vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2; - int fsize = vfbs_[i]->fb_stride * vfbs_[i]->height * (vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2); - if ((vfbs_[i]->fb_address | 0x04000000) == src && size == fsize) { - // A framebuffer matched! - knownFramebufferRAMCopies_.insert(std::pair(src, dst)); + if (Memory::IsVRAMAddress(src) && Memory::IsRAMAddress(dst)) { + for (size_t i = 0; i < vfbs_.size(); i++) { + int bpp = vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2; + int fsize = vfbs_[i]->fb_stride * vfbs_[i]->height * (vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2); + if (MaskedEqual(vfbs_[i]->fb_address, src) && size == fsize) { + // A framebuffer matched! + knownFramebufferRAMCopies_.insert(std::pair(src, dst)); + } } } diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index f90a01deb8..0d4bc08e19 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1964,7 +1964,7 @@ 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)) { + if (framebufferManager_.MayIntersectFramebuffer(src) || framebufferManager_.MayIntersectFramebuffer(dest)) { framebufferManager_.NotifyFramebufferCopy(src, dest, size); } } From cec8195e97b29c808cdd2da09e5b7860eedd26c9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:10:06 -0700 Subject: [PATCH 06/22] Cleanup reporting of block/memcpy transfers. --- GPU/GLES/Framebuffer.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 67e208bc00..6ab750c82e 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1834,9 +1834,8 @@ void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { if (srcBuffer == dstBuffer) { WARN_LOG_REPORT_ONCE(dstsrccpy, G3D, "Intra-buffer memcpy (not supported) %08x -> %08x", src, dst); } else { - WARN_LOG_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x", src, dst); + WARN_LOG_REPORT_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy (not supported) %08x -> %08x", src, dst); // Just do the blit! - // TODO: Possibly take bpp into account somehow if games are doing really crazy things? // if (g_Config.bBlockTransferGPU) { // BlitFramebuffer_(dstBuffer, 0, 0, srcBuffer, 0, 0, srcBuffer->width, srcBuffer->height, 0); // } @@ -1847,7 +1846,7 @@ void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { // if (g_Config.bBlockTransferGPU) { // } } else if (srcBuffer && g_Config.iRenderingMode == FB_BUFFERED_MODE) { - WARN_LOG_ONCE(btdcpy, G3D, "Memcpy fbo download %08x -> %08x", src, dst); + WARN_LOG_REPORT_ONCE(btdcpy, G3D, "Memcpy fbo download (not supported) %08x -> %08x", src, dst); // if (g_Config.bBlockTransferGPU) { // ReadFramebufferToMemory(srcBuffer, true, 0, 0, srcBuffer->width, srcBuffer->height); // } @@ -1890,7 +1889,6 @@ bool FramebufferManager::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride } else { WARN_LOG_ONCE(dstnotsrc, G3D, "Inter-buffer block transfer %08x -> %08x", srcBasePtr, dstBasePtr); // Just do the blit! - // TODO: Possibly take bpp into account somehow if games are doing really crazy things? if (g_Config.bBlockTransferGPU) { BlitFramebuffer_(dstBuffer, dstX, dstY, srcBuffer, srcX, srcY, width, height, bpp); return true; // No need to actually do the memory copy behind, probably. @@ -1945,7 +1943,7 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, bpp); if (dstBuffer && !srcBuffer) { - WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload (not supported) %08x -> %08x", srcBasePtr, dstBasePtr); + WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); if (g_Config.bBlockTransferGPU) { u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride * bpp, width, height); From cbb3fe0086d152df530b7c5e42bff206a917565e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:12:58 -0700 Subject: [PATCH 07/22] Refactor framebuffer size calculation. --- GPU/GLES/Framebuffer.cpp | 10 +++++++--- GPU/GLES/Framebuffer.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 6ab750c82e..7b28b3bf83 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -837,7 +837,7 @@ void FramebufferManager::DoSetRenderFrameBuffer() { glEnable(GL_DITHER); // why? currentRenderVfb_ = vfb; - u32 byteSize = vfb->fb_stride * vfb->height * (vfb->format == GE_FORMAT_8888 ? 4 : 2); + u32 byteSize = FramebufferByteSize(vfb); if (fb_address + byteSize > framebufRangeEnd_) { framebufRangeEnd_ = ((fb_address + byteSize) & 0x3FFFFFFF) | 0x04000000; } @@ -1808,7 +1808,7 @@ void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { if (Memory::IsVRAMAddress(src) && Memory::IsRAMAddress(dst)) { for (size_t i = 0; i < vfbs_.size(); i++) { int bpp = vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2; - int fsize = vfbs_[i]->fb_stride * vfbs_[i]->height * (vfbs_[i]->format == GE_FORMAT_8888 ? 4 : 2); + int fsize = FramebufferByteSize(vfbs_[i]); if (MaskedEqual(vfbs_[i]->fb_address, src) && size == fsize) { // A framebuffer matched! knownFramebufferRAMCopies_.insert(std::pair(src, dst)); @@ -1853,11 +1853,15 @@ void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { } } +u32 FramebufferManager::FramebufferByteSize(const VirtualFramebuffer *vfb) const { + return vfb->fb_stride * vfb->height * (vfb->format == GE_FORMAT_8888 ? 4 : 2); +} + void FramebufferManager::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int bpp) const { for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; const u32 vfb_address = 0x04000000 | vfb->fb_address; - const u32 vfb_size = vfb->fb_stride * vfb->height * (vfb->format == GE_FORMAT_8888 ? 4 : 2); + const u32 vfb_size = FramebufferByteSize(vfb); if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) { dstY += (dstBasePtr - vfb_address) / (dstStride * bpp); dstBuffer = vfb; diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index c2ed32c8b5..fc9ff3a2ab 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -223,6 +223,7 @@ private: void DestroyDraw2DProgram(); void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int bpp) const; + u32 FramebufferByteSize(const VirtualFramebuffer *vfb) const; void SetNumExtraFBOs(int num); From 2e1afcf08d9b31687e5b787d67eee7f79c0935ac Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:24:09 -0700 Subject: [PATCH 08/22] When there are multiple copies, don't redownload. This should improve performance when there are lots of little transfers. --- GPU/GLES/Framebuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 7b28b3bf83..5cfd49e049 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1904,7 +1904,7 @@ bool FramebufferManager::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride return false; } else if (srcBuffer) { WARN_LOG_ONCE(btd, G3D, "Block transfer download %08x -> %08x", srcBasePtr, dstBasePtr); - if (g_Config.bBlockTransferGPU) { + if (g_Config.bBlockTransferGPU && (srcBuffer == currentRenderVfb_ || !srcBuffer->memoryUpdated)) { ReadFramebufferToMemory(srcBuffer, true, srcX, srcY, width, height); } return false; // Let the bit copy happen From 89b3ead30d552a0efb2a875266108bb2cdde8616 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:45:31 -0700 Subject: [PATCH 09/22] If multiple framebuffers match, offset correctly. --- GPU/GLES/Framebuffer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 5cfd49e049..aa8f030238 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1858,19 +1858,24 @@ u32 FramebufferManager::FramebufferByteSize(const VirtualFramebuffer *vfb) const } void FramebufferManager::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int bpp) const { + int dstYOffset = 0; + int srcYOffset = 0; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; const u32 vfb_address = 0x04000000 | vfb->fb_address; const u32 vfb_size = FramebufferByteSize(vfb); if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) { - dstY += (dstBasePtr - vfb_address) / (dstStride * bpp); + dstYOffset = (dstBasePtr - vfb_address) / (dstStride * bpp); dstBuffer = vfb; } if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { - srcY += (srcBasePtr - vfb_address) / (srcStride * bpp); + srcYOffset = (srcBasePtr - vfb_address) / (srcStride * bpp); srcBuffer = vfb; } } + + dstY += dstYOffset; + srcY += srcYOffset; } bool FramebufferManager::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp) { From 24ebc0d987195900e3236f2e8982780cbe27d32b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:46:42 -0700 Subject: [PATCH 10/22] Flip framebuffer y correctly. And here I just corrected another commit doing this wrong a few weeks ago. Shame. --- GPU/GLES/Framebuffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index aa8f030238..a129b2656e 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1310,7 +1310,8 @@ void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int int dstY1 = dstY2 + h * dstYFactor; if (flip) { - std::swap(dstY1, dstY2); + dstY1 = dst->renderHeight - dstY1; + dstY2 = dst->renderHeight - dstY2; } #ifdef MAY_HAVE_GLES3 From c3efa05c9e93be0130a4817fe279f22c02c19cf3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 17:51:12 -0700 Subject: [PATCH 11/22] Correct end framebuf range check. --- GPU/GLES/Framebuffer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index a129b2656e..a9f898ddab 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -838,8 +838,9 @@ void FramebufferManager::DoSetRenderFrameBuffer() { currentRenderVfb_ = vfb; u32 byteSize = FramebufferByteSize(vfb); - if (fb_address + byteSize > framebufRangeEnd_) { - framebufRangeEnd_ = ((fb_address + byteSize) & 0x3FFFFFFF) | 0x04000000; + u32 fb_address_mem = (fb_address & 0x3FFFFFFF) | 0x04000000; + if (fb_address_mem + byteSize > framebufRangeEnd_) { + framebufRangeEnd_ = fb_address_mem + byteSize; } INFO_LOG(SCEGE, "Creating FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); From c42d04829faaaf7f4320a2784b8b85c8fc896341 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 18:18:26 -0700 Subject: [PATCH 12/22] Detect if the game uses block transfer slivers. In that case, it's better to read it once, than multiple times. Improves performance and graphics in Grand Knights History. --- GPU/GLES/Framebuffer.cpp | 25 ++++++++++++++++++++++++- GPU/GLES/Framebuffer.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index a9f898ddab..502cf3c03f 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -328,6 +328,7 @@ FramebufferManager::FramebufferManager() : usePostShader_(false), postShaderAtOutputResolution_(false), resized_(false), + gameUsesSequentialCopies_(false), framebufRangeEnd_(0) #ifndef USING_GLES2 , @@ -1240,7 +1241,29 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s #endif } - vfb->memoryUpdated = true; + if (gameUsesSequentialCopies_) { + // Ignore the x/y/etc., read the entire thing. + x = 0; + y = 0; + w = vfb->width; + h = vfb->height; + } + if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { + vfb->memoryUpdated = true; + } else { + const static int FREQUENT_SEQUENTIAL_COPIES = 3; + static int frameLastCopy = 0; + static u32 bufferLastCopy = 0; + static int copiesThisFrame = 0; + if (frameLastCopy != gpuStats.numFlips || bufferLastCopy != vfb->fb_address) { + frameLastCopy = gpuStats.numFlips; + bufferLastCopy = vfb->fb_address; + copiesThisFrame = 0; + } + if (++copiesThisFrame > FREQUENT_SEQUENTIAL_COPIES) { + gameUsesSequentialCopies_ = true; + } + } BlitFramebuffer_(nvfb, x, y, vfb, x, y, w, h, 0, true); // PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index fc9ff3a2ab..b457d18b9d 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -272,6 +272,7 @@ private: bool resized_; bool useBufferedRendering_; bool updateVRAM_; + bool gameUsesSequentialCopies_; // The range of PSP memory that may contain FBOs. So we can skip iterating. u32 framebufRangeEnd_; From 8228bee6bf590445be3f9d48b0d28fb985e55483 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 19:48:46 -0700 Subject: [PATCH 13/22] Download only the necessary subareas. Performance improvement in Tactics Ogre. --- GPU/GLES/Framebuffer.cpp | 20 +++++++++++++------- GPU/GLES/Framebuffer.h | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 502cf3c03f..c0e1e75a6f 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1270,13 +1270,13 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s // PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs #ifdef USING_GLES2 - PackFramebufferSync_(nvfb); + PackFramebufferSync_(nvfb, x, y, w, h); #else if (gl_extensions.PBO_ARB && gl_extensions.OES_texture_npot) { if (!sync) { PackFramebufferAsync_(nvfb); } else { - PackFramebufferSync_(nvfb); + PackFramebufferSync_(nvfb, x, y, w, h); } } #endif @@ -1615,7 +1615,7 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) { #endif -void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb) { +void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (vfb->fbo) { fbo_bind_for_read(vfb->fbo); } else { @@ -1638,7 +1638,13 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb) { if (!convert) { packed = (GLubyte *)Memory::GetPointer(fb_address); } else { // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address - packed = (GLubyte *)malloc(bufSize * sizeof(GLubyte)); + u32 neededSize = (u32)bufSize * sizeof(GLubyte); + if (!convBuf_ || convBufSize_ < neededSize) { + delete [] convBuf_; + convBuf_ = new u8[neededSize]; + convBufSize_ = neededSize; + } + packed = convBuf_; } if (packed) { @@ -1652,12 +1658,12 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb) { glfmt = GL_BGRA_EXT; } #endif - glReadPixels(0, 0, vfb->fb_stride, vfb->height, glfmt, GL_UNSIGNED_BYTE, packed); + int byteOffset = y * vfb->fb_stride * 4; + glReadPixels(0, y, vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed + byteOffset); // LogReadPixelsError(glGetError()); if (convert) { - ConvertFromRGBA8888(Memory::GetPointer(fb_address), packed, vfb->fb_stride, vfb->height, vfb->format); - free(packed); + ConvertFromRGBA8888(Memory::GetPointer(fb_address + byteOffset), packed + byteOffset, vfb->fb_stride, h, vfb->format); } } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index b457d18b9d..1402175ee0 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -245,7 +245,7 @@ private: #ifndef USING_GLES2 void PackFramebufferAsync_(VirtualFramebuffer *vfb); #endif - void PackFramebufferSync_(VirtualFramebuffer *vfb); + void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); // Used by DrawPixels unsigned int drawPixelsTex_; From 8275f613cef4eb96da675edd3f677c086d8599b0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 20:05:28 -0700 Subject: [PATCH 14/22] UpdateMemory() now returns whether to skip the cpy. This way it can do it itself as necessary in the right order. --- Core/HLE/ReplaceTables.cpp | 38 +++++++++++++++++++-------------- Core/HLE/sceDmac.cpp | 9 ++++---- Core/HLE/sceKernelInterrupt.cpp | 9 +++++--- GPU/Directx9/GPU_DX9.cpp | 3 ++- GPU/Directx9/GPU_DX9.h | 2 +- GPU/GLES/GLES_GPU.cpp | 11 +++++++--- GPU/GLES/GLES_GPU.h | 2 +- GPU/GPUInterface.h | 2 +- GPU/Null/NullGpu.cpp | 3 ++- GPU/Null/NullGpu.h | 2 +- GPU/Software/SoftGpu.cpp | 3 ++- GPU/Software/SoftGpu.h | 2 +- 12 files changed, 52 insertions(+), 34 deletions(-) diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index efccd43dee..e4f6d5bb96 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -104,7 +104,11 @@ static int Replace_memcpy() { u32 destPtr = PARAM(0); u32 srcPtr = PARAM(1); u32 bytes = PARAM(2); - if (bytes != 0) { + bool skip = false; + if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) { + skip = gpu->UpdateMemory(destPtr, srcPtr, bytes); + } + if (!skip && bytes != 0) { u8 *dst = Memory::GetPointerUnchecked(destPtr); u8 *src = Memory::GetPointerUnchecked(srcPtr); memmove(dst, src, bytes); @@ -114,9 +118,6 @@ static int Replace_memcpy() { CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); #endif - if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) { - gpu->UpdateMemory(destPtr, srcPtr, bytes); - } return 10 + bytes / 4; // approximation } @@ -124,7 +125,11 @@ static int Replace_memcpy16() { u32 destPtr = PARAM(0); u32 srcPtr = PARAM(1); u32 bytes = PARAM(2) * 16; - if (bytes != 0) { + bool skip = false; + if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) { + skip = gpu->UpdateMemory(destPtr, srcPtr, bytes); + } + if (!skip && bytes != 0) { u8 *dst = Memory::GetPointerUnchecked(destPtr); u8 *src = Memory::GetPointerUnchecked(srcPtr); memmove(dst, src, bytes); @@ -134,9 +139,6 @@ static int Replace_memcpy16() { CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); #endif - if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) { - gpu->UpdateMemory(destPtr, srcPtr, bytes); - } return 10 + bytes / 4; // approximation } @@ -144,7 +146,11 @@ static int Replace_memmove() { u32 destPtr = PARAM(0); u32 srcPtr = PARAM(1); u32 bytes = PARAM(2); - if (bytes != 0) { + bool skip = false; + if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) { + skip = gpu->UpdateMemory(destPtr, srcPtr, bytes); + } + if (!skip && bytes != 0) { u8 *dst = Memory::GetPointerUnchecked(destPtr); u8 *src = Memory::GetPointerUnchecked(srcPtr); memmove(dst, src, bytes); @@ -154,9 +160,6 @@ static int Replace_memmove() { CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); #endif - if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(srcPtr)) { - gpu->UpdateMemory(destPtr, srcPtr, bytes); - } return 10 + bytes / 4; // approximation } @@ -165,14 +168,17 @@ static int Replace_memset() { u8 *dst = Memory::GetPointerUnchecked(destPtr); u8 value = PARAM(1); u32 bytes = PARAM(2); - memset(dst, value, bytes); + bool skip = false; + if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(destPtr)) { + skip = gpu->UpdateMemory(destPtr, destPtr, bytes); + } + if (!skip) { + memset(dst, value, bytes); + } RETURN(destPtr); #ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); #endif - if (Memory::IsVRAMAddress(destPtr) || Memory::IsVRAMAddress(destPtr)) { - gpu->UpdateMemory(destPtr, destPtr, bytes); - } return 10 + bytes / 4; // approximation } diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 7c6d1dafd8..4fd89dffbb 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -43,16 +43,17 @@ void __DmacDoState(PointerWrap &p) { } int __DmacMemcpy(u32 dst, u32 src, u32 size) { - Memory::Memcpy(dst, Memory::GetPointer(src), size); #ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(src, false, size, currentMIPS->pc); CBreakPoints::ExecMemCheck(dst, true, size, currentMIPS->pc); #endif - src &= ~0x40000000; - dst &= ~0x40000000; + bool skip = false; if (Memory::IsVRAMAddress(src) || Memory::IsVRAMAddress(dst)) { - gpu->UpdateMemory(dst, src, size); + skip = gpu->UpdateMemory(dst, src, size); + } + if (!skip) { + Memory::Memcpy(dst, Memory::GetPointer(src), size); } // This number seems strangely reproducible. diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 0a23d33097..36e57208e3 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -559,11 +559,14 @@ 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); + + bool skip = false; + if (Memory::IsVRAMAddress(src) || Memory::IsVRAMAddress(dst)) { + skip = 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)) + if (!skip && Memory::IsValidAddress(dst) && Memory::IsValidAddress(src) && Memory::IsValidAddress(dst + size - 1) && Memory::IsValidAddress(src + size - 1)) { u8 *dstp = Memory::GetPointer(dst); u8 *srcp = Memory::GetPointer(src); diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 1e26d074ce..1749b11def 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -1315,8 +1315,9 @@ void DIRECTX9_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationTy framebufferManager_.UpdateFromMemory(addr, size); } -void DIRECTX9_GPU::UpdateMemory(u32 dest, u32 src, int size) { +bool DIRECTX9_GPU::UpdateMemory(u32 dest, u32 src, int size) { InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + return false; } void DIRECTX9_GPU::ClearCacheNextFrame() { diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index e0de3b4318..9f6cb86253 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -46,7 +46,7 @@ public: virtual void BeginFrame(); virtual void UpdateStats(); virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type); - virtual void UpdateMemory(u32 dest, u32 src, int size); + virtual bool UpdateMemory(u32 dest, u32 src, int size); virtual void ClearCacheNextFrame(); virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders. diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 0d4bc08e19..20772d038a 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1960,13 +1960,18 @@ 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); - +bool GLES_GPU::UpdateMemory(u32 dest, u32 src, int size) { // Track stray copies of a framebuffer in RAM. MotoGP does this. if (framebufferManager_.MayIntersectFramebuffer(src) || framebufferManager_.MayIntersectFramebuffer(dest)) { + // TODO: Copy in the right order. + Memory::Memcpy(dest, Memory::GetPointer(src), size); framebufferManager_.NotifyFramebufferCopy(src, dest, size); + InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + return true; } + + InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + return false; } void GLES_GPU::ClearCacheNextFrame() { diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index 930b5153c6..fcfd771d40 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -44,7 +44,7 @@ public: virtual void BeginFrame(); virtual void UpdateStats(); virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type); - virtual void UpdateMemory(u32 dest, u32 src, int size); + virtual bool UpdateMemory(u32 dest, u32 src, int size); virtual void ClearCacheNextFrame(); virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders. diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 62d9bae30a..f069022bae 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -227,7 +227,7 @@ public: // If size = -1, invalidate everything. virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type) = 0; // Update either RAM from VRAM, or VRAM from RAM... or even VRAM from VRAM. - virtual void UpdateMemory(u32 dest, u32 src, int size) = 0; + virtual bool UpdateMemory(u32 dest, u32 src, int size) = 0; // Will cause the texture cache to be cleared at the start of the next frame. virtual void ClearCacheNextFrame() = 0; diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index bfb5f2167a..5b881e86e4 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -657,7 +657,8 @@ void NullGPU::InvalidateCache(u32 addr, int size, GPUInvalidationType type) { // Nothing to invalidate. } -void NullGPU::UpdateMemory(u32 dest, u32 src, int size) { +bool NullGPU::UpdateMemory(u32 dest, u32 src, int size) { // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + return false; } diff --git a/GPU/Null/NullGpu.h b/GPU/Null/NullGpu.h index 050588cdd8..db236f002c 100644 --- a/GPU/Null/NullGpu.h +++ b/GPU/Null/NullGpu.h @@ -34,7 +34,7 @@ public: virtual void CopyDisplayToOutput() {} virtual void UpdateStats(); virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type); - virtual void UpdateMemory(u32 dest, u32 src, int size); + virtual bool UpdateMemory(u32 dest, u32 src, int size); virtual void ClearCacheNextFrame() {}; virtual void DeviceLost() {} diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 5e13362ff1..994a9dc307 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -851,12 +851,13 @@ void SoftGPU::InvalidateCache(u32 addr, int size, GPUInvalidationType type) // Nothing to invalidate. } -void SoftGPU::UpdateMemory(u32 dest, u32 src, int size) +bool SoftGPU::UpdateMemory(u32 dest, u32 src, int size) { // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); // Let's just be safe. framebufferDirty_ = true; + return false; } bool SoftGPU::FramebufferDirty() { diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 940fd1669c..038d79e9bd 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -59,7 +59,7 @@ public: virtual void CopyDisplayToOutput(); virtual void UpdateStats(); virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type); - virtual void UpdateMemory(u32 dest, u32 src, int size); + virtual bool UpdateMemory(u32 dest, u32 src, int size); virtual void ClearCacheNextFrame() {}; virtual void DeviceLost() {} From 365012ec58926c875b0485ab79e667a79bf282ab Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 20:23:11 -0700 Subject: [PATCH 15/22] Fix block transfer upload. Wasn't working properly, rendered to the wrong target. No longer need the UpdateFromMemory() thing, now. --- GPU/GLES/Framebuffer.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index c0e1e75a6f..181cd1e0a8 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1954,15 +1954,6 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, return; } - // TODO: This can probably just be handled by a normal block transfer upload, no? - if (Memory::IsRAMAddress(srcBasePtr) && MayIntersectFramebuffer(dstBasePtr)) { - // TODO: This causes glitches in Tactics Ogre if we don't implement both ways (which will probably be slow...) - // The main thing this helps is videos, which will have a matching stride, and zero x/y. - if (dstStride == srcStride && dstY == 0 && dstX == 0 && srcX == 0 && srcY == 0) { - UpdateFromMemory(dstBasePtr, (dstY + height) * dstStride * bpp, true); - } - } - // A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to // the backbuffer. Detect this and have the framebuffermanager draw the pixels. @@ -1986,7 +1977,13 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); if (g_Config.bBlockTransferGPU) { u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; - DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride * bpp, width, height); + fbo_bind_as_render_target(dstBuffer->fbo); + DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride, width, height); + if (currentRenderVfb_) { + fbo_bind_as_render_target(currentRenderVfb_->fbo); + } else { + fbo_unbind(); + } } } } From 9195019872c8be8d570f838b9e05bf0c25579d1f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 20:35:27 -0700 Subject: [PATCH 16/22] Allow memcpy gpu downloads. Fixes FF4 battle transition effect, Trails in the Sky menu, etc. --- GPU/GLES/Framebuffer.cpp | 22 +++++++++++++++++----- GPU/GLES/Framebuffer.h | 2 +- GPU/GLES/GLES_GPU.cpp | 10 ++++++---- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 181cd1e0a8..d37bfd726f 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1834,7 +1834,11 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size, bool safe) { } } -void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { +bool FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { + if (!(g_Config.iRenderingMode == FB_BUFFERED_MODE)) { + return false; + } + // MotoGP workaround if (Memory::IsVRAMAddress(src) && Memory::IsRAMAddress(dst)) { for (size_t i = 0; i < vfbs_.size(); i++) { @@ -1871,16 +1875,24 @@ void FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { // BlitFramebuffer_(dstBuffer, 0, 0, srcBuffer, 0, 0, srcBuffer->width, srcBuffer->height, 0); // } } + Memory::Memcpy(dst, Memory::GetPointer(src), size); + return true; } else if (dstBuffer) { WARN_LOG_REPORT_ONCE(btucpy, G3D, "Memcpy fbo upload (not supported) %08x -> %08x", src, dst); // Here we should just draw the pixels into the buffer. // if (g_Config.bBlockTransferGPU) { // } + Memory::Memcpy(dst, Memory::GetPointer(src), size); + return true; } else if (srcBuffer && g_Config.iRenderingMode == FB_BUFFERED_MODE) { - WARN_LOG_REPORT_ONCE(btdcpy, G3D, "Memcpy fbo download (not supported) %08x -> %08x", src, dst); - // if (g_Config.bBlockTransferGPU) { - // ReadFramebufferToMemory(srcBuffer, true, 0, 0, srcBuffer->width, srcBuffer->height); - // } + WARN_LOG_REPORT_ONCE(btdcpy, G3D, "Memcpy fbo download %08x -> %08x", src, dst); + if (g_Config.bBlockTransferGPU) { + // TODO: Validate x/y/w/h based on size and offset? + ReadFramebufferToMemory(srcBuffer, true, 0, 0, srcBuffer->width, srcBuffer->height); + } + return false; + } else { + return false; } } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 1402175ee0..e23ae84cd1 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -210,7 +210,7 @@ public: return true; } - void NotifyFramebufferCopy(u32 src, u32 dest, int size); + bool NotifyFramebufferCopy(u32 src, u32 dest, int size); void DestroyFramebuf(VirtualFramebuffer *vfb); diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 20772d038a..d455a8c164 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1963,10 +1963,12 @@ void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType t bool GLES_GPU::UpdateMemory(u32 dest, u32 src, int size) { // Track stray copies of a framebuffer in RAM. MotoGP does this. if (framebufferManager_.MayIntersectFramebuffer(src) || framebufferManager_.MayIntersectFramebuffer(dest)) { - // TODO: Copy in the right order. - Memory::Memcpy(dest, Memory::GetPointer(src), size); - framebufferManager_.NotifyFramebufferCopy(src, dest, size); - InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + if (!framebufferManager_.NotifyFramebufferCopy(src, dest, size)) { + Memory::Memcpy(dest, Memory::GetPointer(src), size); + InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + } else { + InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + } return true; } From 9894b38a1d740535d3b813ae0c549a85450f839e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 22:00:51 -0700 Subject: [PATCH 17/22] Support memcpy uploads to fbos. Makes Vempire look right (has never worked before in any rendering mode.) --- GPU/GLES/Framebuffer.cpp | 31 +++++++++++++++++++++++-------- GPU/GLES/GLES_GPU.cpp | 6 +++++- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index d37bfd726f..49133a0b9f 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1878,13 +1878,25 @@ bool FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { Memory::Memcpy(dst, Memory::GetPointer(src), size); return true; } else if (dstBuffer) { - WARN_LOG_REPORT_ONCE(btucpy, G3D, "Memcpy fbo upload (not supported) %08x -> %08x", src, dst); - // Here we should just draw the pixels into the buffer. - // if (g_Config.bBlockTransferGPU) { - // } - Memory::Memcpy(dst, Memory::GetPointer(src), size); - return true; - } else if (srcBuffer && g_Config.iRenderingMode == FB_BUFFERED_MODE) { + WARN_LOG_REPORT_ONCE(btucpy, G3D, "Memcpy fbo upload %08x -> %08x", src, dst); + if (g_Config.bBlockTransferGPU) { + const u8 *srcBase = Memory::GetPointerUnchecked(src); + fbo_bind_as_render_target(dstBuffer->fbo); + // TODO: Validate x/y/w/h based on size and offset? + DrawPixels(dstBuffer, 0, 0, srcBase, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height); + dstBuffer->dirtyAfterDisplay = true; + if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) + dstBuffer->reallyDirtyAfterDisplay = true; + if (currentRenderVfb_) { + fbo_bind_as_render_target(currentRenderVfb_->fbo); + } else { + fbo_unbind(); + } + // This is a memcpy, let's still copy just in case. + return false; + } + return false; + } else if (srcBuffer) { WARN_LOG_REPORT_ONCE(btdcpy, G3D, "Memcpy fbo download %08x -> %08x", src, dst); if (g_Config.bBlockTransferGPU) { // TODO: Validate x/y/w/h based on size and offset? @@ -1988,9 +2000,12 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, if (dstBuffer && !srcBuffer) { WARN_LOG_REPORT_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); if (g_Config.bBlockTransferGPU) { - u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; + const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; fbo_bind_as_render_target(dstBuffer->fbo); DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride, width, height); + dstBuffer->dirtyAfterDisplay = true; + if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) + dstBuffer->reallyDirtyAfterDisplay = true; if (currentRenderVfb_) { fbo_bind_as_render_target(currentRenderVfb_->fbo); } else { diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index d455a8c164..3dab6a593f 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1956,7 +1956,11 @@ void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType t textureCache_.InvalidateAll(type); if (type != GPU_INVALIDATE_ALL && framebufferManager_.MayIntersectFramebuffer(addr)) { - framebufferManager_.UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE); + // If we're doing block transfers, we shouldn't need this, and it'll only confuse us. + // Vempire invalidates (with writeback) after drawing, but before blitting. + if (!g_Config.bBlockTransferGPU || type == GPU_INVALIDATE_SAFE) { + framebufferManager_.UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE); + } } } From e4dc702d688e5a331241bf3c2a3d54269c022315 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 22:08:15 -0700 Subject: [PATCH 18/22] Schedule the FBO blit during multithreading. This was it's properly ordered with draws, and it runs on the right GL context. --- GPU/GLES/GLES_GPU.cpp | 27 +++++++++++++++++++++++---- GPU/GLES/GLES_GPU.h | 1 + GPU/GPUInterface.h | 7 +++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 3dab6a593f..dbda6f590b 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -661,6 +661,10 @@ void GLES_GPU::ProcessEvent(GPUEvent ev) { InvalidateCacheInternal(ev.invalidate_cache.addr, ev.invalidate_cache.size, ev.invalidate_cache.type); break; + case GPU_EVENT_FB_MEMCPY: + UpdateMemoryInternal(ev.fb_memcpy.dst, ev.fb_memcpy.src, ev.fb_memcpy.size); + break; + default: GPUCommon::ProcessEvent(ev); } @@ -1964,14 +1968,29 @@ void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType t } } +void GLES_GPU::UpdateMemoryInternal(u32 dest, u32 src, int size) { + if (!framebufferManager_.NotifyFramebufferCopy(src, dest, size)) { + Memory::Memcpy(dest, Memory::GetPointer(src), size); + InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + } else { + InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + } +} + bool GLES_GPU::UpdateMemory(u32 dest, u32 src, int size) { // Track stray copies of a framebuffer in RAM. MotoGP does this. if (framebufferManager_.MayIntersectFramebuffer(src) || framebufferManager_.MayIntersectFramebuffer(dest)) { - if (!framebufferManager_.NotifyFramebufferCopy(src, dest, size)) { - Memory::Memcpy(dest, Memory::GetPointer(src), size); - InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + if (IsOnSeparateCPUThread()) { + GPUEvent ev(GPU_EVENT_FB_MEMCPY); + ev.fb_memcpy.dst = dest; + ev.fb_memcpy.src = src; + ev.fb_memcpy.size = size; + ScheduleEvent(ev); + + // This is a memcpy, so we need to wait for it to complete. + SyncThread(); } else { - InvalidateCache(dest, size, GPU_INVALIDATE_HINT); + UpdateMemoryInternal(dest, src, size); } return true; } diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index fcfd771d40..872811c913 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -151,6 +151,7 @@ private: void InitClearInternal(); void BeginFrameInternal(); void CopyDisplayToOutputInternal(); + void UpdateMemoryInternal(u32 dest, u32 src, int size); void InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type); static CommandInfo cmdInfo_[256]; diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index f069022bae..65f9d8a351 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -163,6 +163,7 @@ enum GPUEventType { GPU_EVENT_INVALIDATE_CACHE, GPU_EVENT_FINISH_EVENT_LOOP, GPU_EVENT_SYNC_THREAD, + GPU_EVENT_FB_MEMCPY, }; struct GPUEvent { @@ -175,6 +176,12 @@ struct GPUEvent { int size; GPUInvalidationType type; } invalidate_cache; + // GPU_EVENT_FB_MEMCPY + struct { + u32 dst; + u32 src; + int size; + } fb_memcpy; }; operator GPUEventType() const { From 4f03f693b67abc18029ee617ad66716ce73c6a0e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 22:35:14 -0700 Subject: [PATCH 19/22] Mark texture as needing reset after upload. --- GPU/GLES/Framebuffer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 49133a0b9f..a90696fe69 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1892,6 +1892,7 @@ bool FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { } else { fbo_unbind(); } + gstate_c.textureChanged = TEXCHANGE_PARAMSONLY; // This is a memcpy, let's still copy just in case. return false; } @@ -2011,6 +2012,7 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, } else { fbo_unbind(); } + gstate_c.textureChanged = TEXCHANGE_PARAMSONLY; } } } From a29d2e418e468173fe5c1c53f3367bb403e63381 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 May 2014 22:57:07 -0700 Subject: [PATCH 20/22] When multiple framebuffers match, use nearest. --- GPU/GLES/Framebuffer.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index a90696fe69..56efde619f 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1914,24 +1914,34 @@ u32 FramebufferManager::FramebufferByteSize(const VirtualFramebuffer *vfb) const } void FramebufferManager::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int bpp) const { - int dstYOffset = 0; - int srcYOffset = 0; + u32 dstYOffset = -1; + u32 srcYOffset = -1; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; const u32 vfb_address = 0x04000000 | vfb->fb_address; const u32 vfb_size = FramebufferByteSize(vfb); if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) { - dstYOffset = (dstBasePtr - vfb_address) / (dstStride * bpp); - dstBuffer = vfb; + const u32 yOffset = (dstBasePtr - vfb_address) / (dstStride * bpp); + if (yOffset < dstYOffset) { + dstYOffset = yOffset; + dstBuffer = vfb; + } } if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { - srcYOffset = (srcBasePtr - vfb_address) / (srcStride * bpp); - srcBuffer = vfb; + const u32 yOffset = (srcBasePtr - vfb_address) / (srcStride * bpp); + if (yOffset < srcYOffset) { + srcYOffset = yOffset; + srcBuffer = vfb; + } } } - dstY += dstYOffset; - srcY += srcYOffset; + if (dstYOffset != (u32)-1) { + dstY += dstYOffset; + } + if (srcYOffset >= (u32)-1) { + srcY += srcYOffset; + } } bool FramebufferManager::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp) { From 6243d0e8de79aa515d103fb406e1c2b8c85e8ea6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 26 May 2014 00:52:06 -0700 Subject: [PATCH 21/22] Account for bpp mismatch in xfer upload. --- GPU/GLES/Framebuffer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 56efde619f..0eb4f29838 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -2013,7 +2013,9 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, if (g_Config.bBlockTransferGPU) { const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; fbo_bind_as_render_target(dstBuffer->fbo); - DrawPixels(dstBuffer, dstX, dstY, srcBase, dstBuffer->format, srcStride, width, height); + int dstBpp = dstBuffer->format == GE_FORMAT_8888 ? 4 : 2; + float dstXFactor = (float)bpp / dstBpp; + DrawPixels(dstBuffer, dstX * dstXFactor, dstY, srcBase, dstBuffer->format, srcStride * dstXFactor, width * dstXFactor, height); dstBuffer->dirtyAfterDisplay = true; if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) dstBuffer->reallyDirtyAfterDisplay = true; From ee8a180c410632d3b9c710d6f83328620641b82e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 26 May 2014 01:21:30 -0700 Subject: [PATCH 22/22] Set the viewport on upload so 2x, etc. work. --- GPU/GLES/Framebuffer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 0eb4f29838..dd31e3fd84 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1882,6 +1882,7 @@ bool FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { if (g_Config.bBlockTransferGPU) { const u8 *srcBase = Memory::GetPointerUnchecked(src); fbo_bind_as_render_target(dstBuffer->fbo); + glViewport(0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight); // TODO: Validate x/y/w/h based on size and offset? DrawPixels(dstBuffer, 0, 0, srcBase, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height); dstBuffer->dirtyAfterDisplay = true; @@ -1892,6 +1893,7 @@ bool FramebufferManager::NotifyFramebufferCopy(u32 src, u32 dst, int size) { } else { fbo_unbind(); } + glstate.viewport.restore(); gstate_c.textureChanged = TEXCHANGE_PARAMSONLY; // This is a memcpy, let's still copy just in case. return false; @@ -2015,6 +2017,7 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, fbo_bind_as_render_target(dstBuffer->fbo); int dstBpp = dstBuffer->format == GE_FORMAT_8888 ? 4 : 2; float dstXFactor = (float)bpp / dstBpp; + glViewport(0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight); DrawPixels(dstBuffer, dstX * dstXFactor, dstY, srcBase, dstBuffer->format, srcStride * dstXFactor, width * dstXFactor, height); dstBuffer->dirtyAfterDisplay = true; if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) @@ -2024,6 +2027,7 @@ void FramebufferManager::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, } else { fbo_unbind(); } + glstate.viewport.restore(); gstate_c.textureChanged = TEXCHANGE_PARAMSONLY; } }