From c2e0225aa39a0e98a65f5ebbd600cd9f2aa05e9c Mon Sep 17 00:00:00 2001 From: Nolan Check Date: Mon, 29 Jun 2009 07:30:48 +0000 Subject: [PATCH] Move to new Virtual XFB system which correctly handles games which store multiple XFB's in memory. More OpenGL cleanup. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3591 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Common/Src/PluginVideo.h | 2 +- Source/Core/Core/Src/HW/VideoInterface.cpp | 37 ++- Source/Core/VideoCommon/Src/BPFunctions.h | 2 +- Source/Core/VideoCommon/Src/BPStructs.cpp | 2 +- Source/PluginSpecs/pluginspecs_video.h | 2 +- .../Plugin_VideoDX9/Src/BPFunctions.cpp | 2 +- Source/Plugins/Plugin_VideoDX9/Src/main.cpp | 2 +- .../Plugin_VideoOGL/Src/BPFunctions.cpp | 4 +- .../Src/FramebufferManager.cpp | 210 +++++++++++++++++- .../Plugin_VideoOGL/Src/FramebufferManager.h | 84 +++++++ Source/Plugins/Plugin_VideoOGL/Src/Render.cpp | 142 ++++-------- Source/Plugins/Plugin_VideoOGL/Src/Render.h | 5 +- .../Plugin_VideoOGL/Src/TextureConverter.cpp | 9 +- .../Plugin_VideoOGL/Src/TextureConverter.h | 2 +- Source/Plugins/Plugin_VideoOGL/Src/main.cpp | 17 +- 15 files changed, 389 insertions(+), 133 deletions(-) diff --git a/Source/Core/Common/Src/PluginVideo.h b/Source/Core/Common/Src/PluginVideo.h index 1721899b6d..6b6940fd2c 100644 --- a/Source/Core/Common/Src/PluginVideo.h +++ b/Source/Core/Common/Src/PluginVideo.h @@ -25,7 +25,7 @@ namespace Common { typedef void (__cdecl* TVideo_Prepare)(); typedef void (__cdecl* TVideo_SendFifoData)(u8*,u32); -typedef void (__cdecl* TVideo_UpdateXFB)(u8*, u32, u32, s32, bool); +typedef void (__cdecl* TVideo_UpdateXFB)(u32, u32, u32, s32, bool); typedef bool (__cdecl* TVideo_Screenshot)(const char* filename); typedef void (__cdecl* TVideo_EnterLoop)(); typedef void (__cdecl* TVideo_ExitLoop)(); diff --git a/Source/Core/Core/Src/HW/VideoInterface.cpp b/Source/Core/Core/Src/HW/VideoInterface.cpp index b6fd1d18ac..8d08f1273a 100644 --- a/Source/Core/Core/Src/HW/VideoInterface.cpp +++ b/Source/Core/Core/Src/HW/VideoInterface.cpp @@ -995,6 +995,14 @@ u8* GetXFBPointerTop() return Memory::GetPointer(m_XFBInfoTop.FBB); } +u32 GetXFBPointerTop_GC() +{ + if (m_XFBInfoTop.POFF) + return m_XFBInfoTop.FBB << 5; + else + return m_XFBInfoTop.FBB; +} + u8* GetXFBPointerBottom() { // POFF for XFB bottom is connected to POFF for XFB top @@ -1004,6 +1012,15 @@ u8* GetXFBPointerBottom() return Memory::GetPointer(m_XFBInfoBottom.FBB); } +u32 GetXFBPointerBottom_GC() +{ + // POFF for XFB bottom is connected to POFF for XFB top + if (m_XFBInfoTop.POFF) + return m_XFBInfoBottom.FBB << 5; + else + return m_XFBInfoBottom.FBB; +} + // Screenshot and screen message @@ -1077,7 +1094,7 @@ void Update() if (m_VBeamPos == NextXFBRender) { - u8* xfbPtr = 0; + u32 xfbAddr = 0; int yOffset = 0; if (NextXFBRender == 1) @@ -1086,34 +1103,34 @@ void Update() // TODO: proper VI regs typedef and logic for XFB to work. // eg. Animal Crossing gc have smth in TFBL.XOF bitfield. // "XOF - Horizontal Offset of the left-most pixel within the first word of the fetched picture." - xfbPtr = GetXFBPointerTop(); - _dbg_assert_msg_(VIDEOINTERFACE, xfbPtr, "Bad top XFB address"); + xfbAddr = GetXFBPointerTop_GC(); + _dbg_assert_msg_(VIDEOINTERFACE, xfbAddr, "Bad top XFB address"); } else { NextXFBRender = 1; // Previously checked m_XFBInfoTop.POFF then used m_XFBInfoBottom.FBB, try reverting if there are problems - xfbPtr = GetXFBPointerBottom(); - _dbg_assert_msg_(VIDEOINTERFACE, xfbPtr, "Bad bottom XFB address"); + xfbAddr = GetXFBPointerBottom_GC(); + _dbg_assert_msg_(VIDEOINTERFACE, xfbAddr, "Bad bottom XFB address"); yOffset = -1; } Common::PluginVideo* video = CPluginManager::GetInstance().GetVideo(); - if (xfbPtr && video->IsValid()) + if (xfbAddr && video->IsValid()) { int fbWidth = m_HorizontalStepping.FieldSteps * 16; int fbHeight = (m_HorizontalStepping.FbSteps / m_HorizontalStepping.FieldSteps) * m_VerticalTimingRegister.ACV; - DEBUG_LOG(VIDEOINTERFACE, "(VI->XFBUpdate): ptr: %p | %ix%i | xoff: %i", - xfbPtr, fbWidth, fbHeight, m_XFBInfoTop.XOFF); + DEBUG_LOG(VIDEOINTERFACE, "(VI->XFBUpdate): ptr: %.08X | %ix%i | xoff: %i", + xfbAddr, fbWidth, fbHeight, m_XFBInfoTop.XOFF); if (Core::GetStartupParameter().bUseDualCore) // scheduled on EmuThread in DC mode - video->Video_UpdateXFB(xfbPtr, fbWidth, fbHeight, yOffset, TRUE); + video->Video_UpdateXFB(xfbAddr, fbWidth, fbHeight, yOffset, TRUE); else // otherwise do it now from here (CPUthread) - video->Video_UpdateXFB(xfbPtr, fbWidth, fbHeight, yOffset, FALSE); + video->Video_UpdateXFB(xfbAddr, fbWidth, fbHeight, yOffset, FALSE); } } diff --git a/Source/Core/VideoCommon/Src/BPFunctions.h b/Source/Core/VideoCommon/Src/BPFunctions.h index c3dd4ccc01..c22e9aa0b6 100644 --- a/Source/Core/VideoCommon/Src/BPFunctions.h +++ b/Source/Core/VideoCommon/Src/BPFunctions.h @@ -48,7 +48,7 @@ void SetColorMask(const Bypass &bp); float GetRendererTargetScaleX(); float GetRendererTargetScaleY(); void CopyEFB(const Bypass &bp, const TRectangle &rc, const u32 &address, const bool &fromZBuffer, const bool &isIntensityFmt, const u32 ©fmt, const bool &scaleByHalf); -void RenderToXFB(const Bypass &bp, const TRectangle &multirc, const float &yScale, const float &xfbLines, u8* pXFB, const u32 &dstWidth, const u32 &dstHeight); +void RenderToXFB(const Bypass &bp, const TRectangle &multirc, const float &yScale, const float &xfbLines, u32 xfbAddr, const u32 &dstWidth, const u32 &dstHeight); void ClearScreen(const Bypass &bp, const TRectangle &multirc); void RestoreRenderState(const Bypass &bp); u8 *GetPointer(const u32 &address); diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index e0b0c9bca2..19540bfca3 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -220,7 +220,7 @@ void BPWritten(const Bypass& bp) const float yScale = bpmem.dispcopyyscale / 256.0f; const float xfbLines = ((bpmem.copyTexSrcWH.y + 1.0f) * yScale); RenderToXFB(bp, multirc, yScale, xfbLines, - Memory_GetPtr(bpmem.copyTexDest << 5), + bpmem.copyTexDest << 5, bpmem.copyMipMapStrideChannels << 4, (u32)ceil(xfbLines)); } diff --git a/Source/PluginSpecs/pluginspecs_video.h b/Source/PluginSpecs/pluginspecs_video.h index f25659e7cc..b81964a9e4 100644 --- a/Source/PluginSpecs/pluginspecs_video.h +++ b/Source/PluginSpecs/pluginspecs_video.h @@ -110,7 +110,7 @@ EXPORT void CALL Video_SendFifoData(u8* _uData, u32 len); // input: pointer to the XFB, width and height of the XFB // output: none // -EXPORT void CALL Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling); +EXPORT void CALL Video_UpdateXFB(u32 _dwXFBAddr, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling); // __________________________________________________________________________________________________ // Function: Video_Screenshot diff --git a/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp index f24bd947e2..8107c28c0f 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp @@ -228,7 +228,7 @@ void CopyEFB(const Bypass &bp, const TRectangle &rc, const u32 &address, const b TextureCache::CopyEFBToRenderTarget(bpmem.copyTexDest<<5, &rec); } -void RenderToXFB(const Bypass &bp, const TRectangle &multirc, const float &yScale, const float &xfbLines, u8* pXFB, const u32 &dstWidth, const u32 &dstHeight) +void RenderToXFB(const Bypass &bp, const TRectangle &multirc, const float &yScale, const float &xfbLines, u32 xfbAddr, const u32 &dstWidth, const u32 &dstHeight) { Renderer::SwapBuffers(); PRIM_LOG("Renderer::SwapBuffers()"); diff --git a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp index 6ba0c3e60d..e53fb226f0 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp @@ -249,7 +249,7 @@ void Shutdown(void) DeInit(); } -void Video_UpdateXFB(u8* /*_pXFB*/, u32 /*_dwWidth*/, u32 /*_dwHeight*/, s32 /*_dwYOffset*/, bool /*scheduling*/) +void Video_UpdateXFB(u32 /*_dwXFBAddr*/, u32 /*_dwWidth*/, u32 /*_dwHeight*/, s32 /*_dwYOffset*/, bool /*scheduling*/) { /* ConvertXFB(tempBuffer, _pXFB, _dwWidth, _dwHeight); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp index 51f4121954..1fe8da65ed 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp @@ -132,9 +132,9 @@ void CopyEFB(const Bypass &bp, const TRectangle &rc, const u32 &address, const b TextureMngr::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc); } -void RenderToXFB(const Bypass &bp, const TRectangle &multirc, const float &yScale, const float &xfbLines, u8* pXFB, const u32 &dstWidth, const u32 &dstHeight) +void RenderToXFB(const Bypass &bp, const TRectangle &multirc, const float &yScale, const float &xfbLines, u32 xfbAddr, const u32 &dstWidth, const u32 &dstHeight) { - Renderer::RenderToXFB(pXFB, multirc, dstWidth, dstHeight); + Renderer::RenderToXFB(xfbAddr, dstWidth, dstHeight, multirc); } void ClearScreen(const Bypass &bp, const TRectangle &multirc) { diff --git a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp index 1c531db68d..529c7fe876 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp @@ -18,6 +18,9 @@ #include "Globals.h" #include "FramebufferManager.h" +#include "TextureConverter.h" +#include "XFB.h" + void FramebufferManager::Init(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples) { m_targetWidth = targetWidth; @@ -156,7 +159,11 @@ void FramebufferManager::Shutdown() glObj[0] = m_resolvedColorTexture; glObj[1] = m_resolvedDepthTexture; - glDeleteTextures(2, glObj); + glObj[2] = m_realXFBSource.texture; + glDeleteTextures(3, glObj); + m_resolvedColorTexture = 0; + m_resolvedDepthTexture = 0; + m_realXFBSource.texture = 0; glObj[0] = m_efbColor; glObj[1] = m_efbDepth; @@ -166,6 +173,28 @@ void FramebufferManager::Shutdown() glDeleteRenderbuffersEXT(2, glObj); m_efbColor = 0; m_efbDepth = 0; + + for (VirtualXFBListType::iterator it = m_virtualXFBList.begin(); it != m_virtualXFBList.end(); ++it) + { + glDeleteTextures(1, &it->xfbSource.texture); + } + m_virtualXFBList.clear(); +} + +void FramebufferManager::CopyToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc) +{ + if (g_Config.bUseXFB) + copyToRealXFB(xfbAddr, dstWidth, dstHeight, sourceRc); + else + copyToVirtualXFB(xfbAddr, dstWidth, dstHeight, sourceRc); +} + +const XFBSource* FramebufferManager::GetXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight) +{ + if (g_Config.bUseXFB) + return getRealXFBSource(xfbAddr, srcWidth, srcHeight); + else + return getVirtualXFBSource(xfbAddr, srcWidth, srcHeight); } GLuint FramebufferManager::GetEFBColorTexture(const TRectangle& sourceRc) const @@ -231,3 +260,182 @@ GLuint FramebufferManager::GetEFBDepthTexture(const TRectangle& sourceRc) const return m_resolvedDepthTexture; } } + +FramebufferManager::VirtualXFBListType::iterator +FramebufferManager::findVirtualXFB(u32 xfbAddr, u32 width, u32 height) +{ + u32 srcLower = xfbAddr; + u32 srcUpper = xfbAddr + 2 * width * height; + + VirtualXFBListType::iterator it; + for (it = m_virtualXFBList.begin(); it != m_virtualXFBList.end(); ++it) + { + u32 dstLower = it->xfbAddr; + u32 dstUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight; + + if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper)) + return it; + } + + // That address is not in the Virtual XFB list. + return m_virtualXFBList.end(); +} + +void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc) +{ + u8* pXFB = Memory_GetPtr(xfbAddr); + if (!pXFB) + { + WARN_LOG(VIDEO, "Tried to copy to invalid XFB address"); + return; + } + + XFB_Write(pXFB, sourceRc, dstWidth, dstHeight); +} + +void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc) +{ + GLuint xfbTexture; + + VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, dstWidth, dstHeight); + + if (it != m_virtualXFBList.end()) + { + // Overwrite an existing Virtual XFB. + + it->xfbAddr = xfbAddr; + it->xfbWidth = dstWidth; + it->xfbHeight = dstHeight; + + it->xfbSource.texWidth = m_targetWidth; + it->xfbSource.texHeight = m_targetHeight; + it->xfbSource.sourceRc = sourceRc; + + xfbTexture = it->xfbSource.texture; + + // Move this Virtual XFB to the front of the list. + m_virtualXFBList.splice(m_virtualXFBList.begin(), m_virtualXFBList, it); + } + else + { + // Create a new Virtual XFB and place it at the front of the list. + + glGenTextures(1, &xfbTexture); + + if (m_msaaSamples > 1) + { + // In MSAA mode, allocate the texture image here. In non-MSAA mode, + // the image will be allocated by glCopyTexImage2D (later). + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfbTexture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, m_targetWidth, m_targetHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + } + + VirtualXFB newVirt; + + newVirt.xfbAddr = xfbAddr; + newVirt.xfbWidth = dstWidth; + newVirt.xfbHeight = dstHeight; + + newVirt.xfbSource.texture = xfbTexture; + newVirt.xfbSource.texWidth = m_targetWidth; + newVirt.xfbSource.texHeight = m_targetHeight; + newVirt.xfbSource.sourceRc = sourceRc; + + // Add the new Virtual XFB to the list + + if (m_virtualXFBList.size() >= MAX_VIRTUAL_XFB) + { + // List overflowed; delete the oldest. + glDeleteTextures(1, &m_virtualXFBList.back().xfbSource.texture); + m_virtualXFBList.pop_back(); + } + + m_virtualXFBList.push_front(newVirt); + } + + // Copy EFB to XFB texture + + if (m_msaaSamples <= 1) + { + // Just copy the EFB directly. + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfbTexture); + glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, 0, 0, m_targetWidth, m_targetHeight, 0); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + } + else + { + // OpenGL cannot copy directly from a multisampled framebuffer, so use + // EXT_framebuffer_blit. + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_efbFramebuffer); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_xfbFramebuffer); + + // Bind texture. + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, xfbTexture, 0); + GL_REPORT_FBO_ERROR(); + + glBlitFramebufferEXT( + 0, 0, m_targetWidth, m_targetHeight, + 0, 0, m_targetWidth, m_targetHeight, + GL_COLOR_BUFFER_BIT, GL_NEAREST + ); + + // Unbind texture. + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0); + + // Return to EFB. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); + } +} + +const XFBSource* FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight) +{ + m_realXFBSource.texWidth = XFB_WIDTH; + m_realXFBSource.texHeight = XFB_HEIGHT; + + m_realXFBSource.sourceRc.left = 0; + m_realXFBSource.sourceRc.top = 0; + m_realXFBSource.sourceRc.right = srcWidth; + m_realXFBSource.sourceRc.bottom = srcHeight; + + if (!m_realXFBSource.texture) + { + glGenTextures(1, &m_realXFBSource.texture); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_realXFBSource.texture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, XFB_WIDTH, XFB_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + } + + // Decode YUYV data from GameCube RAM + TextureConverter::DecodeToTexture(xfbAddr, srcWidth, srcHeight, m_realXFBSource.texture); + + return &m_realXFBSource; +} + +const XFBSource* FramebufferManager::getVirtualXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight) +{ + if (m_virtualXFBList.size() == 0) + { + // No Virtual XFBs available. + return NULL; + } + + VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, srcWidth, srcHeight); + if (it == m_virtualXFBList.end()) + { + // Virtual XFB is not in the list, so return the most recently rendered + // one. + it = m_virtualXFBList.begin(); + } + + return &it->xfbSource; +} diff --git a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h index 9a8561ab37..1ec6314a6e 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h @@ -18,8 +18,65 @@ #ifndef _FRAMEBUFFERMANAGER_H_ #define _FRAMEBUFFERMANAGER_H_ +#include #include "GLUtil.h" +// On the GameCube, the game sends a request for the graphics processor to +// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM +// called the XFB (External Framebuffer). The size and location of the XFB is +// decided at the time of the copy, and the format is always YUYV. The video +// interface is given a pointer to the XFB, which will be decoded and +// displayed on the TV. +// +// There are two ways for Dolphin to emulate this: +// +// Real XFB mode: +// +// Dolphin will behave like the GameCube and encode the EFB to +// a portion of GameCube RAM. The emulated video interface will decode the data +// for output to the screen. +// +// Advantages: Behaves exactly like the GameCube. +// Disadvantages: Resolution will be limited. +// +// Virtual XFB mode: +// +// When a request is made to copy the EFB to an XFB, Dolphin +// will remember the RAM location and size of the XFB in a Virtual XFB list. +// The video interface will look up the XFB in the list and use the enhanced +// data stored there, if available. +// +// Advantages: Enables high resolution graphics, better than real hardware. +// Disadvantages: If the GameCube CPU writes directly to the XFB (which is +// possible but uncommon), the Virtual XFB will not capture this information. + +// There may be multiple XFBs in GameCube RAM. This is the maximum number to +// virtualize. +const int MAX_VIRTUAL_XFB = 4; + +inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return ( + (aLower >= bLower && aLower < bUpper) || + (aUpper >= bLower && aUpper < bUpper) || + (bLower >= aLower && bLower < aUpper) || + (bUpper >= aLower && bUpper < aUpper) + ); +} + +struct XFBSource +{ + XFBSource() : + texture(0) + {} + + GLuint texture; + int texWidth; + int texHeight; + + TRectangle sourceRc; +}; + class FramebufferManager { @@ -38,6 +95,12 @@ public: void Init(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples); void Shutdown(); + // sourceRc is in GL target coordinates, not GameCube EFB coordinates! + // TODO: Clean that up. + void CopyToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc); + + const XFBSource* GetXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight); + // To get the EFB in texture form, these functions may have to transfer // the EFB to a resolved texture first. GLuint GetEFBColorTexture(const TRectangle& sourceRc) const; @@ -47,6 +110,25 @@ public: private: + struct VirtualXFB + { + // Address and size in GameCube RAM + u32 xfbAddr; + u32 xfbWidth; + u32 xfbHeight; + + XFBSource xfbSource; + }; + + typedef std::list VirtualXFBListType; + + VirtualXFBListType::iterator findVirtualXFB(u32 xfbAddr, u32 width, u32 height); + + void copyToRealXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc); + void copyToVirtualXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc); + const XFBSource* getRealXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight); + const XFBSource* getVirtualXFBSource(u32 xfbAddr, u32 srcWidth, u32 srcHeight); + int m_targetWidth; int m_targetHeight; int m_msaaSamples; @@ -62,6 +144,8 @@ private: GLuint m_resolvedDepthTexture; GLuint m_xfbFramebuffer; // Only used in MSAA mode + XFBSource m_realXFBSource; // Only used in Real XFB mode + VirtualXFBListType m_virtualXFBList; // Only used in Virtual XFB mode }; diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index e4a5823afb..12c5d206d3 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -93,11 +93,6 @@ static FILE* f_pFrameDump; static int s_MSAASamples = 1; static int s_MSAACoverageSamples = 0; -// TODO: Move these to FramebufferManager -static TRectangle s_efbSourceRc; -static GLuint s_xfbFramebuffer = 0; // Only used when multisampling is on -static GLuint s_xfbTexture = 0; - static bool s_bHaveStencilBuffer = false; static bool s_bHaveFramebufferBlit = false; static bool s_bHaveCoverageMSAA = false; @@ -305,22 +300,6 @@ bool Renderer::Init() if (s_targetheight < EFB_HEIGHT) s_targetheight = EFB_HEIGHT; - // Create the XFB texture - glGenTextures(1, &s_xfbTexture); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_xfbTexture); - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - SetDefaultRectTexParams(); - - // Bind it to an XFB framebuffer if MSAA is used - if (s_MSAASamples > 1) - { - glGenFramebuffersEXT(1, &s_xfbFramebuffer); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_xfbFramebuffer); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, s_xfbTexture, 0); - - GL_REPORT_FBO_ERROR(); - } - if (GL_REPORT_ERROR() != GL_NO_ERROR) bSuccess = false; @@ -384,14 +363,6 @@ void Renderer::Shutdown(void) s_framebufferManager.Shutdown(); - // Note: OpenGL delete functions automatically ignore if parameter is 0 - - glDeleteFramebuffersEXT(1, &s_xfbFramebuffer); - s_xfbFramebuffer = 0; - - glDeleteTextures(1, &s_xfbTexture); - s_xfbTexture = 0; - #ifdef _WIN32 if(s_bAVIDumping) { AVIDump::Stop(); @@ -689,68 +660,45 @@ void ComputeBackbufferRectangle(TRectangle *rc) rc->bottom = YOffset + ceil(FloatGLHeight); } -void Renderer::DecodeFromXFB(u8* xfbInRam, u32 dstWidth, u32 dstHeight, s32 yOffset) -{ - TextureConverter::DecodeToTexture(xfbInRam + yOffset*(XFB_WIDTH*2), dstWidth, dstHeight, s_xfbTexture); -} +// TODO: Use something less ugly than an Evil Global Variable. +// Also, protect this structure with a mutex. +extern volatile struct // Comes from main.cpp +{ + u32 xfbAddr; + u32 width; + u32 height; + s32 yOffset; +} tUpdateXFBArgs; -void Renderer::RenderToXFB(u8* xfbInRam, const TRectangle& sourceRc, u32 dstWidth, u32 dstHeight) +void Renderer::RenderToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc) { - // Make sure the previous contents made it to the screen if requested - if (g_XFBUpdateRequested) + u32 aLower = xfbAddr; + u32 aUpper = xfbAddr + 2 * dstWidth * dstHeight; + u32 bLower = tUpdateXFBArgs.xfbAddr; + u32 bUpper = tUpdateXFBArgs.xfbAddr + 2 * tUpdateXFBArgs.width * tUpdateXFBArgs.height; + + // If we're about to write into a requested XFB, make sure the previous + // contents make it to the screen first. + if (g_XFBUpdateRequested && addrRangesOverlap(aLower, aUpper, bLower, bUpper)) { Video_UpdateXFB(NULL, 0, 0, 0, FALSE); } - // TODO: Move this logic to FramebufferManager - if (g_Config.bUseXFB) - { - s_efbSourceRc.left = 0; - s_efbSourceRc.top = 0; - s_efbSourceRc.right = GetTargetWidth(); - s_efbSourceRc.bottom = GetTargetHeight(); - XFB_Write(xfbInRam, sourceRc, dstWidth, dstHeight); - } - else - { - // Renderer::Swap will use the source rectangle saved here - s_efbSourceRc = sourceRc; - - if (s_MSAASamples > 1) - { - // Cannot use glCopyTexImage2D on multisampled framebuffers, so - // EXT_framebuffer_blit must be used - - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_framebufferManager.GetEFBFramebuffer()); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, s_xfbFramebuffer); - - glBlitFramebufferEXT( - 0, 0, s_targetwidth, s_targetheight, - 0, 0, s_targetwidth, s_targetheight, - GL_COLOR_BUFFER_BIT, GL_NEAREST - ); - - // Return to the EFB - SetFramebuffer(0); - } - else - { - // Just copy the EFB directly - SetFramebuffer(0); - - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_xfbTexture); - glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, 0, 0, s_targetwidth, s_targetheight, 0); - - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); - } - } + s_framebufferManager.CopyToXFB(xfbAddr, dstWidth, dstHeight, sourceRc); } -// This function has the final picture. We -// adjust the aspect ratio here. -void Renderer::Swap() +// This function has the final picture. We adjust the aspect ratio here. +// yOffset is used to eliminate interlacing jitter in Real XFB mode. +void Renderer::Swap(u32 xfbAddr, u32 srcWidth, u32 srcHeight, s32 yOffset) { + const XFBSource* xfbSource = s_framebufferManager.GetXFBSource(xfbAddr, srcWidth, srcHeight); + if (!xfbSource) + { + WARN_LOG(VIDEO, "Failed to get video for this frame"); + return; + } + OpenGL_Update(); // just updates the render window position and the backbuffer size DVSTARTPROFILE(); @@ -760,28 +708,24 @@ void Renderer::Swap() ComputeBackbufferRectangle(&back_rc); float u_max; - float v_min = 0.f; + float v_min; float v_max; - if (g_Config.bUseXFB) + if (g_Config.bAutoScale) { - u_max = XFB_WIDTH; - v_min = 0; - v_max = XFB_HEIGHT; - } - else if (g_Config.bAutoScale) - { - u_max = (s_efbSourceRc.right - s_efbSourceRc.left); - v_min = (float)GetTargetHeight() - (s_efbSourceRc.bottom - s_efbSourceRc.top); - v_max = (float)GetTargetHeight(); + u_max = (xfbSource->sourceRc.right - xfbSource->sourceRc.left); + v_min = (float)xfbSource->texHeight - (xfbSource->sourceRc.bottom - xfbSource->sourceRc.top); + v_max = (float)xfbSource->texHeight; } else { - u_max = (float)GetTargetWidth(); - v_max = (float)GetTargetHeight(); + u_max = (float)xfbSource->texWidth; + v_max = (float)xfbSource->texHeight; } + v_min -= yOffset; + v_max -= yOffset; // Tell the OSD Menu about the current internal resolution - OSDInternalW = s_efbSourceRc.GetWidth(); OSDInternalH = s_efbSourceRc.bottom; + OSDInternalW = xfbSource->sourceRc.GetWidth(); OSDInternalH = xfbSource->sourceRc.bottom; // Make sure that the wireframe setting doesn't screw up the screen copy. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -806,7 +750,7 @@ void Renderer::Swap() // Texture map s_xfbTexture onto the main buffer glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_RECTANGLE_ARB); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_xfbTexture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfbSource->texture); // Use linear filtering. glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -850,7 +794,7 @@ void Renderer::Swap() s_criticalScreenshot.Enter(); // Save screenshot - SaveRenderTarget(s_sScreenshotName.c_str(), s_efbSourceRc.right, s_efbSourceRc.bottom, (int)(v_min)); + SaveRenderTarget(s_sScreenshotName.c_str(), xfbSource->sourceRc.right, xfbSource->sourceRc.bottom, (int)(v_min)); // Reset settings s_sScreenshotName = ""; s_bScreenshot = false; @@ -868,8 +812,8 @@ void Renderer::Swap() glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_framebufferManager.GetEFBFramebuffer()); s_criticalScreenshot.Enter(); - int w = s_efbSourceRc.right; - int h = s_efbSourceRc.bottom; + int w = xfbSource->sourceRc.right; + int h = xfbSource->sourceRc.bottom; int t = (int)(v_min); u8 *data = (u8 *) malloc(3 * w * h); glPixelStorei(GL_PACK_ALIGNMENT, 1); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.h b/Source/Plugins/Plugin_VideoOGL/Src/Render.h index de187c4bdd..e0c04759e4 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.h @@ -87,11 +87,10 @@ public: static void FlipImageData(u8 *data, int w, int h); static bool SaveRenderTarget(const char *filename, int w, int h, int YOffset = 0); - static void DecodeFromXFB(u8* xfbInRam, u32 dstWidth, u32 dstHeight, s32 yOffset); - static void RenderToXFB(u8* xfbInRam, const TRectangle& sourceRc, u32 dstWidth, u32 dstHeight); + static void RenderToXFB(u32 xfbAddr, u32 dstWidth, u32 dstHeight, const TRectangle& sourceRc); // Finish up the current frame, print some stats - static void Swap(); + static void Swap(u32 xfbAddr, u32 srcWidth, u32 srcHeight, s32 yOffset); }; void ComputeBackbufferRectangle(TRectangle *rc); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp index c4bec2beaa..fde7e07e20 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp @@ -308,8 +308,15 @@ void EncodeToRamYUYV(GLuint srcTexture, const TRectangle& sourceRc, // Should be scale free. -void DecodeToTexture(u8* srcAddr, int srcWidth, int srcHeight, GLuint destTexture) +void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture) { + u8* srcAddr = Memory_GetPtr(xfbAddr); + if (!srcAddr) + { + WARN_LOG(VIDEO, "Tried to decode from invalid memory address"); + return; + } + Renderer::ResetGLState(); float srcFormatFactor = 0.5f; diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.h b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.h index 43b7c3d365..f9f3785117 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.h @@ -35,7 +35,7 @@ void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, void EncodeToRamYUYV(GLuint srcTexture, const TRectangle& sourceRc, u8* destAddr, int dstWidth, int dstHeight); -void DecodeToTexture(u8* srcAddr, int srcWidth, int srcHeight, GLuint destTexture); +void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture); } diff --git a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp index 953d486ca8..599fafee2e 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp @@ -415,22 +415,22 @@ void Video_AddMessage(const char* pstr, u32 milliseconds) // TODO: Protect this structure with a mutex. -static volatile struct +volatile struct { - u8* pXFB; + u32 xfbAddr; u32 width; u32 height; s32 yOffset; } tUpdateXFBArgs; // Run from the CPU thread (from VideoInterface.cpp) for certain homebrew games only -void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling) +void Video_UpdateXFB(u32 _dwXFBAddr, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling) { if (s_PluginInitialized) { if (scheduling) // From CPU in DC mode { - tUpdateXFBArgs.pXFB = _pXFB; + tUpdateXFBArgs.xfbAddr = _dwXFBAddr; tUpdateXFBArgs.width = _dwWidth; tUpdateXFBArgs.height = _dwHeight; tUpdateXFBArgs.yOffset = _dwYOffset; @@ -441,20 +441,17 @@ void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, boo { g_XFBUpdateRequested = FALSE; - if (!_pXFB) + if (!_dwXFBAddr) { // From graphics thread in DC mode - _pXFB = tUpdateXFBArgs.pXFB; + _dwXFBAddr = tUpdateXFBArgs.xfbAddr; _dwWidth = tUpdateXFBArgs.width; _dwHeight = tUpdateXFBArgs.height; _dwYOffset = tUpdateXFBArgs.yOffset; } - if (g_Config.bUseXFB) - Renderer::DecodeFromXFB(_pXFB, _dwWidth, _dwHeight, _dwYOffset); - // TODO: Use real XFB source parameters based on VI settings - Renderer::Swap(); + Renderer::Swap(_dwXFBAddr, _dwWidth, _dwHeight, g_Config.bUseXFB ? _dwYOffset : 0); g_VideoInitialize.pCopiedToXFB(); }