diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index b06a5840b9..3291f94754 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1974,63 +1974,6 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return true; } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - // Here let's assume they don't intersect - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_5551: // ABGR 1555 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_4444: // ABGR 4444 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } - } -} - // This function takes an already correctly-sized framebuffer and packs it into RAM. // Does not need to account for scaling. // Color conversion is currently done on CPU but should theoretically be done on GPU. @@ -2043,43 +1986,19 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int return; } - int possibleH = std::max(vfb->height - y, 0); - if (h > possibleH) { - h = possibleH; - } + const u32 fb_address = (0x04000000) | vfb->fb_address; - // Pixel size always 4 here because we always request RGBA8888 - u32 bufSize = vfb->fb_stride * h * 4; - u32 fb_address = 0x04000000 | vfb->fb_address; + Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); + const int dstBpp = (int)DataFormatSizeInBytes(destFormat); - bool convert = vfb->format != GE_FORMAT_8888; - const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; - const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width)); + const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); - int dstByteOffset = y * vfb->fb_stride * dstBpp; - u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); + // We always need to convert from the framebuffer native format. + // Right now that's always 8888. + DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); - u8 *packed = nullptr; - if (!convert) { - packed = (u8 *)dst; - } else { - // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address - if (!convBuf_ || convBufSize_ < bufSize) { - delete[] convBuf_; - convBuf_ = new u8[bufSize]; - convBufSize_ = bufSize; - } - packed = convBuf_; - } - - if (packed) { - DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); - int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, w, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); - if (convert) { - ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); - } - } + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); } void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 552ae1cee1..62befe6913 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -402,10 +402,6 @@ protected: int bloomHack_ = 0; bool trueColor_ = false; - // Used to convert readbacks. - u8 *convBuf_ = nullptr; - u32 convBufSize_ = 0; - // Used by post-processing shaders std::vector extraFBOs_; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 376870a7a2..3eb62e0bcd 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -204,6 +204,7 @@ FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) : FramebufferManagerCommon(draw), drawPixelsTex_(0), drawPixelsTexFormat_(GE_FORMAT_INVALID), + convBuf_(nullptr), draw2dprogram_(nullptr), postShaderProgram_(nullptr), stencilUploadProgram_(nullptr), @@ -720,6 +721,63 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, CHECK_GL_ERROR_IF_DEBUG(); } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const u32 *src32 = (const u32 *)src; + + if (format == GE_FORMAT_8888) { + u32 *dst32 = (u32 *)dst; + if (src == dst) { + return; + } else { + // Here let's assume they don't intersect + for (u32 y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + u16 *dst16 = (u16 *)dst; + switch (format) { + case GE_FORMAT_565: // BGR 565 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_5551: // ABGR 1555 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_4444: // ABGR 4444 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_8888: + case GE_FORMAT_INVALID: + // Not possible. + break; + } + } +} + void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; @@ -842,7 +900,48 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - FramebufferManagerCommon::PackFramebufferSync_(vfb, x, y, w, h); + if (!vfb->fbo) { + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); + return; + } + + int possibleH = std::max(vfb->height - y, 0); + if (h > possibleH) { + h = possibleH; + } + + // Pixel size always 4 here because we always request RGBA8888 + u32 bufSize = vfb->fb_stride * h * 4; + u32 fb_address = 0x04000000 | vfb->fb_address; + + bool convert = vfb->format != GE_FORMAT_8888; + const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; + const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width)); + + int dstByteOffset = y * vfb->fb_stride * dstBpp; + u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); + + u8 *packed = nullptr; + if (!convert) { + packed = (u8 *)dst; + } else { + // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address + if (!convBuf_ || convBufSize_ < bufSize) { + delete [] convBuf_; + convBuf_ = new u8[bufSize]; + convBufSize_ = bufSize; + } + packed = convBuf_; + } + + if (packed) { + DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); + int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); + if (convert) { + ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); + } + } // TODO: Move this into Thin3d. if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index d5f922c852..f867f833d6 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -112,6 +112,8 @@ private: int drawPixelsTexW_; int drawPixelsTexH_; + u8 *convBuf_; + u32 convBufSize_; GLSLProgram *draw2dprogram_; GLSLProgram *plainColorProgram_; GLSLProgram *postShaderProgram_;