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
This commit is contained in:
parent
188fa9b2cb
commit
c2e0225aa3
|
@ -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)();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,65 @@
|
|||
#ifndef _FRAMEBUFFERMANAGER_H_
|
||||
#define _FRAMEBUFFERMANAGER_H_
|
||||
|
||||
#include <list>
|
||||
#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<VirtualXFB> 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
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue