Display buffer flips are now triggered by the CPU thread through the Video_UpdateXFB function. This is a fairly significant change of behavior, but it improves performance.

Also, render target code is cleaned up. Post-processing shaders now work with multisampling and Real-XFB mode.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3545 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Nolan Check 2009-06-26 08:57:53 +00:00
parent a7eb7bbf00
commit ade9479005
9 changed files with 240 additions and 420 deletions

View File

@ -1079,9 +1079,6 @@ void Update()
u8* xfbPtr = 0; u8* xfbPtr = 0;
int yOffset = 0; int yOffset = 0;
// (mb2) hack: We request XFB updates from CPUthread (here) only when homebrews use directly XFB without FIFO and CP
if (!Core::GetStartupParameter().bUseDualCore || CommandProcessor::IsCommandProcessorNotUsed())
{
if (NextXFBRender == 1) if (NextXFBRender == 1)
{ {
NextXFBRender = LinesPerField; NextXFBRender = LinesPerField;
@ -1118,7 +1115,6 @@ void Update()
video->Video_UpdateXFB(xfbPtr, fbWidth, fbHeight, yOffset, FALSE); video->Video_UpdateXFB(xfbPtr, fbWidth, fbHeight, yOffset, FALSE);
} }
} }
}
// check INT_PRERETRACE // check INT_PRERETRACE
if (m_InterruptRegister[0].VCT == m_VBeamPos) if (m_InterruptRegister[0].VCT == m_VBeamPos)

View File

@ -143,7 +143,6 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
if (g_XFBUpdateRequested) if (g_XFBUpdateRequested)
{ {
Video_UpdateXFB(NULL, 0, 0, 0, FALSE); Video_UpdateXFB(NULL, 0, 0, 0, FALSE);
video_initialize.pCopiedToXFB();
} }
// check if we are able to run this buffer // check if we are able to run this buffer

View File

@ -134,26 +134,7 @@ void CopyEFB(const Bypass &bp, const TRectangle &rc, const u32 &address, const b
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, u8* pXFB, const u32 &dstWidth, const u32 &dstHeight)
{ {
// EFB to XFB Renderer::RenderToXFB(pXFB, multirc, dstWidth, dstHeight);
if (g_Config.bUseXFB)
{
XFB_Write(pXFB, multirc, dstWidth, dstHeight);
// FIXME: we draw XFB from here in DC mode.
// Bad hack since we can have multiple EFB to XFB copy before a draw.
// Plus we should use width and height from VI regs (see VI->Update()).
// Dixit donkopunchstania for the info.
//DebugLog("(EFB to XFB->XFB_Draw): ptr: %08x | %ix%i", (u32)pXFB, dstWidth, dstHeight);
if (g_VideoInitialize.bUseDualCore)
XFB_Draw(pXFB, dstWidth, dstHeight, 0);
}
else
{
// Hm, we need to compensate for the fact that the copy may be bigger than what is displayed.
// Seen in Spartan Warrior. Not sure how to deal with it yet.
Renderer::Swap(multirc);
}
g_VideoInitialize.pCopiedToXFB();
} }
void ClearScreen(const Bypass &bp, const TRectangle &multirc) void ClearScreen(const Bypass &bp, const TRectangle &multirc)
{ {

View File

@ -49,6 +49,7 @@
#include "VertexLoaderManager.h" #include "VertexLoaderManager.h"
#include "VertexLoader.h" #include "VertexLoader.h"
#include "PostProcessing.h" #include "PostProcessing.h"
#include "TextureConverter.h"
#include "XFB.h" #include "XFB.h"
#include "OnScreenDisplay.h" #include "OnScreenDisplay.h"
#include "Timer.h" #include "Timer.h"
@ -122,6 +123,10 @@ static GLuint s_DepthBuffer = 0;
static GLuint s_ResolvedRenderTarget = 0; static GLuint s_ResolvedRenderTarget = 0;
static GLuint s_ResolvedDepthTarget = 0; static GLuint s_ResolvedDepthTarget = 0;
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_bHaveStencilBuffer = false;
static bool s_bHaveFramebufferBlit = false; static bool s_bHaveFramebufferBlit = false;
static bool s_bHaveCoverageMSAA = false; static bool s_bHaveCoverageMSAA = false;
@ -319,6 +324,12 @@ bool Renderer::Init()
if (s_targetheight < EFB_HEIGHT) if (s_targetheight < EFB_HEIGHT)
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();
glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer); glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer);
if (s_uFramebuffer == 0) { if (s_uFramebuffer == 0) {
ERROR_LOG(VIDEO, "failed to create the renderbufferDoes your video card support OpenGL 2.x?"); ERROR_LOG(VIDEO, "failed to create the renderbufferDoes your video card support OpenGL 2.x?");
@ -365,6 +376,17 @@ bool Renderer::Init()
} }
else else
{ {
// Create XFB framebuffer for transferring the multisampled EFB to the
// XFB texture
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();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
// MSAA rendertarget init. // MSAA rendertarget init.
// First set up the boring multisampled rendertarget. // First set up the boring multisampled rendertarget.
glGenRenderbuffersEXT(1, &s_RenderTarget); glGenRenderbuffersEXT(1, &s_RenderTarget);
@ -466,12 +488,7 @@ bool Renderer::Init()
if (!InitializeGL()) if (!InitializeGL())
return false; return false;
XFB_Init();
return glGetError() == GL_NO_ERROR && bSuccess; return glGetError() == GL_NO_ERROR && bSuccess;
// Now save the actual settings
s_targetwidth = (int)OpenGL_GetBackbufferWidth();
s_targetheight = (int)OpenGL_GetBackbufferHeight();
} }
void Renderer::Shutdown(void) void Renderer::Shutdown(void)
@ -479,24 +496,28 @@ void Renderer::Shutdown(void)
delete s_pfont; delete s_pfont;
s_pfont = 0; s_pfont = 0;
XFB_Shutdown();
if (g_cgcontext) { if (g_cgcontext) {
cgDestroyContext(g_cgcontext); cgDestroyContext(g_cgcontext);
g_cgcontext = 0; g_cgcontext = 0;
} }
if (s_RenderTarget) {
glDeleteTextures(1, &s_RenderTarget); // Note: OpenGL delete functions automatically ignore if parameter is 0
s_RenderTarget = 0;
}
if (s_DepthTarget) {
glDeleteRenderbuffersEXT(1, &s_DepthTarget);
s_DepthTarget = 0;
}
if (s_uFramebuffer) {
glDeleteFramebuffersEXT(1, &s_uFramebuffer); glDeleteFramebuffersEXT(1, &s_uFramebuffer);
s_uFramebuffer = 0; s_uFramebuffer = 0;
}
glDeleteTextures(1, &s_RenderTarget);
s_RenderTarget = 0;
glDeleteRenderbuffersEXT(1, &s_DepthTarget);
s_DepthTarget = 0;
glDeleteFramebuffersEXT(1, &s_xfbFramebuffer);
s_xfbFramebuffer = 0;
glDeleteTextures(1, &s_xfbTexture);
s_xfbTexture = 0;
#ifdef _WIN32 #ifdef _WIN32
if(s_bAVIDumping) { if(s_bAVIDumping) {
AVIDump::Stop(); AVIDump::Stop();
@ -579,6 +600,9 @@ float Renderer::GetTargetScaleY()
// Various supporting functions // Various supporting functions
void Renderer::SetRenderTarget(GLuint targ) void Renderer::SetRenderTarget(GLuint targ)
{ {
if (!targ && s_MSAASamples > 1)
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, s_RenderTarget);
else
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,
targ != 0 ? targ : s_RenderTarget, 0); targ != 0 ? targ : s_RenderTarget, 0);
} }
@ -603,9 +627,14 @@ void Renderer::ResetGLState()
glDisable(GL_FRAGMENT_PROGRAM_ARB); glDisable(GL_FRAGMENT_PROGRAM_ARB);
} }
void UpdateViewport();
void Renderer::RestoreGLState() void Renderer::RestoreGLState()
{ {
// Gets us back into a more game-like state. // Gets us back into a more game-like state.
UpdateViewport();
if (bpmem.genMode.cullmode > 0) glEnable(GL_CULL_FACE); if (bpmem.genMode.cullmode > 0) glEnable(GL_CULL_FACE);
if (bpmem.zmode.testenable) glEnable(GL_DEPTH_TEST); if (bpmem.zmode.testenable) glEnable(GL_DEPTH_TEST);
if (bpmem.zmode.updateenable) glDepthMask(GL_TRUE); if (bpmem.zmode.updateenable) glDepthMask(GL_TRUE);
@ -849,11 +878,66 @@ void ComputeBackbufferRectangle(TRectangle *rc)
rc->bottom = YOffset + ceil(FloatGLHeight); 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);
}
void Renderer::RenderToXFB(u8* xfbInRam, const TRectangle& sourceRc, u32 dstWidth, u32 dstHeight)
{
// Make sure the previous contents made it to the screen if requested
if (g_XFBUpdateRequested)
{
Video_UpdateXFB(NULL, 0, 0, 0, FALSE);
}
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_uFramebuffer);
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);
}
}
}
// This function has the final picture if the XFB functions are not used. We // This function has the final picture. We
// adjust the aspect ratio here. // adjust the aspect ratio here.
void Renderer::Swap(const TRectangle& rc) void Renderer::Swap()
{ {
OpenGL_Update(); // just updates the render window position and the backbuffer size OpenGL_Update(); // just updates the render window position and the backbuffer size
DVSTARTPROFILE(); DVSTARTPROFILE();
@ -868,8 +952,8 @@ void Renderer::Swap(const TRectangle& rc)
float v_max; float v_max;
if (g_Config.bAutoScale) if (g_Config.bAutoScale)
{ {
u_max = (rc.right - rc.left); u_max = (s_efbSourceRc.right - s_efbSourceRc.left);
v_min = (float)GetTargetHeight() - (rc.bottom - rc.top); v_min = (float)GetTargetHeight() - (s_efbSourceRc.bottom - s_efbSourceRc.top);
v_max = (float)GetTargetHeight(); v_max = (float)GetTargetHeight();
} }
else else
@ -879,37 +963,14 @@ void Renderer::Swap(const TRectangle& rc)
} }
// Tell the OSD Menu about the current internal resolution // Tell the OSD Menu about the current internal resolution
OSDInternalW = rc.right; OSDInternalH = rc.bottom; OSDInternalW = s_efbSourceRc.GetWidth(); OSDInternalH = s_efbSourceRc.bottom;
// Make sure that the wireframe setting doesn't screw up the screen copy. // Make sure that the wireframe setting doesn't screw up the screen copy.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// --------------------------------------------------------------------- // Textured triangles are necessary because of post-processing shaders
// Resolve the multisampled rendertarget into the normal one.
// ¯¯¯¯¯¯¯¯¯¯¯¯¯ // Disable all other stages
if (/*s_bHaveFramebufferBlit*/ s_MSAASamples > 1)
{
// Use framebuffer blit to stretch screen.
// No messing around with annoying glBegin and viewports, plus can support multisampling.
if (s_MSAASamples > 1)
{
ResolveAndGetRenderTarget(rc);
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uResolvedFramebuffer);
}
else
{
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
}
// Draw to the window buffer with bilinear filtering
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
glBlitFramebufferEXT(0, v_min, u_max, v_max,
back_rc.left, back_rc.top, back_rc.right, back_rc.bottom,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
else
{
// No framebuffer_blit extension - crappy gfx card! Fall back to plain texturing solution.
// Disable all other stages.
for (int i = 1; i < 8; ++i) for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i); TextureMngr::DisableStage(i);
@ -920,28 +981,18 @@ void Renderer::Swap(const TRectangle& rc)
GL_REPORT_ERRORD(); GL_REPORT_ERRORD();
// Copy the framebuffer to screen. // Copy the framebuffer to screen.
// TODO: Use glBlitFramebufferEXT.
// Render to the real buffer now. // Render to the real buffer now.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer
// Texture map s_RenderTargets[s_curtarget] onto the main buffer // Texture map s_xfbTexture onto the main buffer
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_RECTANGLE_ARB); glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_xfbTexture);
// Use linear filtering. // Use linear filtering.
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
/*
static const float vtx_data[8] = {-1, -1, -1, 1, 1, 1, 1, -1};
const float uv_data[8] = {0, v_min, 0, v_max, u_max, v_max, u_max, v_min};
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, (void *)vtx_data);
glTexCoordPointer(2, GL_FLOAT, 0, (void *)uv_data);
glDrawArrays(GL_QUADS, 0, 4);
*/
// We must call ApplyShader here even if no post proc is selected - it takes // We must call ApplyShader here even if no post proc is selected - it takes
// care of disabling it in that case. It returns false in case of no post processing. // care of disabling it in that case. It returns false in case of no post processing.
if (PostProcessing::ApplyShader()) if (PostProcessing::ApplyShader())
@ -968,7 +1019,6 @@ void Renderer::Swap(const TRectangle& rc)
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0); TextureMngr::DisableStage(0);
}
// Wireframe // Wireframe
if (g_Config.bWireFrame) if (g_Config.bWireFrame)
@ -985,7 +1035,7 @@ void Renderer::Swap(const TRectangle& rc)
s_criticalScreenshot.Enter(); s_criticalScreenshot.Enter();
// Save screenshot // Save screenshot
SaveRenderTarget(s_sScreenshotName.c_str(), rc.right, rc.bottom, (int)(v_min)); SaveRenderTarget(s_sScreenshotName.c_str(), s_efbSourceRc.right, s_efbSourceRc.bottom, (int)(v_min));
// Reset settings // Reset settings
s_sScreenshotName = ""; s_sScreenshotName = "";
s_bScreenshot = false; s_bScreenshot = false;
@ -1006,8 +1056,8 @@ void Renderer::Swap(const TRectangle& rc)
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer); glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
s_criticalScreenshot.Enter(); s_criticalScreenshot.Enter();
int w = rc.right; int w = s_efbSourceRc.right;
int h = rc.bottom; int h = s_efbSourceRc.bottom;
int t = (int)(v_min); int t = (int)(v_min);
u8 *data = (u8 *) malloc(3 * w * h); u8 *data = (u8 *) malloc(3 * w * h);
glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1);
@ -1174,7 +1224,7 @@ void Renderer::SwapBuffers()
stats.ResetFrame(); stats.ResetFrame();
// Render to the framebuffer. // Render to the framebuffer.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); SetFramebuffer(0);
GL_REPORT_ERRORD(); GL_REPORT_ERRORD();
} }

View File

@ -88,8 +88,11 @@ public:
static void FlipImageData(u8 *data, int w, int h); static void FlipImageData(u8 *data, int w, int h);
static bool SaveRenderTarget(const char *filename, int w, int h, int YOffset = 0); 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);
// Finish up the current frame, print some stats // Finish up the current frame, print some stats
static void Swap(const TRectangle& rc); static void Swap();
}; };
void ComputeBackbufferRectangle(TRectangle *rc); void ComputeBackbufferRectangle(TRectangle *rc);

View File

@ -321,6 +321,8 @@ void DecodeToTexture(u8* srcAddr, int srcWidth, int srcHeight, GLuint destTextur
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, destTexture); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, destTexture);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, destTexture, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, destTexture, 0);
GL_REPORT_FBO_ERROR();
for (int i = 1; i < 8; ++i) for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i); TextureMngr::DisableStage(i);
@ -349,10 +351,13 @@ void DecodeToTexture(u8* srcAddr, int srcWidth, int srcHeight, GLuint destTextur
// reset state // reset state
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
TextureMngr::DisableStage(0); TextureMngr::DisableStage(0);
VertexShaderManager::SetViewportChanged(); VertexShaderManager::SetViewportChanged();
Renderer::SetFramebuffer(0);
Renderer::RestoreGLState(); Renderer::RestoreGLState();
GL_REPORT_ERRORD(); GL_REPORT_ERRORD();
} }

View File

@ -25,55 +25,10 @@
// ---------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------
#include "Globals.h" #include "Globals.h"
#include "GLUtil.h" #include "XFB.h"
#include "MemoryUtil.h"
#include "Render.h" #include "Render.h"
#include "TextureMngr.h"
#include "VertexShaderManager.h"
#include "XFBConvert.h"
#include "TextureConverter.h" #include "TextureConverter.h"
#define XFB_USE_SHADERS 1
enum {
XFB_BUF_HEIGHT = 574, //480,
// TODO: figure out what to do with PAL
};
#if XFB_USE_SHADERS
static GLuint xfb_decoded_texture;
static int XFBInitStatus = 0;
static struct
{
u8* pXFB;
u32 width;
u32 height;
s32 yOffset;
} tUpdateXFBArgs;
void XFB_SetUpdateArgs(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset);
void XFB_Init()
{
glGenTextures(1, &xfb_decoded_texture);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfb_decoded_texture);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, XFB_WIDTH, XFB_BUF_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
XFBInitStatus = 1;
}
void XFB_Shutdown()
{
glDeleteTextures(1, &xfb_decoded_texture);
XFBInitStatus = 0;
}
int XFB_isInit()
{
return XFBInitStatus;
}
void XFB_Write(u8 *xfb_in_ram, const TRectangle& sourceRc, u32 dstWd, u32 dstHt) void XFB_Write(u8 *xfb_in_ram, const TRectangle& sourceRc, u32 dstWd, u32 dstHt)
{ {
TRectangle renderSrcRc; TRectangle renderSrcRc;
@ -84,181 +39,3 @@ void XFB_Write(u8 *xfb_in_ram, const TRectangle& sourceRc, u32 dstWd, u32 dstHt)
renderSrcRc.bottom = Renderer::GetTargetHeight() - sourceRc.bottom; renderSrcRc.bottom = Renderer::GetTargetHeight() - sourceRc.bottom;
TextureConverter::EncodeToRamYUYV(Renderer::ResolveAndGetRenderTarget(sourceRc), renderSrcRc, xfb_in_ram, dstWd, dstHt); TextureConverter::EncodeToRamYUYV(Renderer::ResolveAndGetRenderTarget(sourceRc), renderSrcRc, xfb_in_ram, dstWd, dstHt);
} }
// Draw the XFB straight to the OpenGL backbuffer.
void XFB_Draw(u8 *xfb_in_ram, u32 width, u32 height, s32 yOffset)
{
TextureConverter::DecodeToTexture(xfb_in_ram, width, height, xfb_decoded_texture);
OpenGL_Update(); // just updates the render window position and the backbuffer size
Renderer::ResetGLState();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the backbuffer
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfb_decoded_texture);
TRectangle back_rc;
ComputeBackbufferRectangle(&back_rc);
glViewport(back_rc.left, back_rc.top, back_rc.right - back_rc.left, back_rc.bottom - back_rc.top);
GL_REPORT_ERRORD();
float w = (float)width;
float h = (float)height;
float yOff = (float)yOffset;
glBegin(GL_QUADS);
glTexCoord2f(w, 0 - yOff); glVertex2f(1, -1);
glTexCoord2f(w, h - yOff); glVertex2f(1, 1);
glTexCoord2f(0, h - yOff); glVertex2f(-1, 1);
glTexCoord2f(0, 0 - yOff); glVertex2f(-1,-1);
glEnd();
TextureMngr::DisableStage(0);
Renderer::SwapBuffers();
Renderer::RestoreGLState();
GL_REPORT_ERRORD();
}
void XFB_Draw()
{
XFB_Draw(tUpdateXFBArgs.pXFB, tUpdateXFBArgs.width, tUpdateXFBArgs.height, tUpdateXFBArgs.yOffset);
}
void XFB_SetUpdateArgs(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset)
{
tUpdateXFBArgs.pXFB = _pXFB;
tUpdateXFBArgs.width = _dwWidth;
tUpdateXFBArgs.height = _dwHeight;
tUpdateXFBArgs.yOffset = _dwYOffset;
}
#else
static GLuint xfb_texture;
static u8 *xfb_buffer = 0;
static u8 *efb_buffer = 0;
static GLuint s_xfbFrameBuffer = 0;
static GLuint s_xfbRenderBuffer = 0;
void XFB_Init()
{
// used to render XFB
xfb_buffer = new u8[XFB_WIDTH * XFB_BUF_HEIGHT * 4];
memset(xfb_buffer, 0, XFB_WIDTH * XFB_BUF_HEIGHT * 4);
glGenTextures(1, &xfb_texture);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfb_texture);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, XFB_WIDTH, XFB_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, xfb_buffer);
// used to render EFB
glGenFramebuffersEXT(1, &s_xfbFrameBuffer);
glGenRenderbuffersEXT(1, &s_xfbRenderBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_xfbRenderBuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA, nBackbufferWidth, nBackbufferHeight);
// Ensure efb_buffer is aligned.
efb_buffer = (u8 *)AllocateMemoryPages(nBackbufferWidth * nBackbufferHeight * 4);
}
void XFB_Shutdown()
{
glDeleteFramebuffersEXT(1, &s_xfbFrameBuffer);
glDeleteTextures(1, &xfb_texture);
xfb_texture = 0;
delete [] xfb_buffer;
xfb_buffer = 0;
FreeMemoryPages(efb_buffer, nBackbufferWidth * nBackbufferHeight * 4);
}
void XFB_Write(u8 *xfb_in_ram, const TRectangle& sourceRc, u32 dstWd, u32 dstHt)
{
Renderer::SetRenderMode(Renderer::RM_Normal);
Renderer::ResetGLState();
// Switch to XFB frame buffer.
Renderer::SetFramebuffer(s_xfbFrameBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_xfbRenderBuffer);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, s_xfbRenderBuffer);
GL_REPORT_ERRORD();
glViewport(nXoff, nYoff, nBackbufferWidth, nBackbufferHeight);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, Renderer::GetRenderTarget());
TextureMngr::EnableTexRECT(0);
for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i);
GL_REPORT_ERRORD();
glBegin(GL_QUADS);
glTexCoord2f(0, nBackbufferHeight); glVertex2f(-1,-1);
glTexCoord2f(0, 0); glVertex2f(-1,1);
glTexCoord2f(nBackbufferWidth, 0); glVertex2f(1,1);
glTexCoord2f(nBackbufferWidth, nBackbufferHeight); glVertex2f(1,-1);
glEnd();
GL_REPORT_ERRORD();
int width = sourceRc.right - sourceRc.left;
int height = sourceRc.bottom - sourceRc.top;
glReadPixels(sourceRc.left, sourceRc.top, width, height, GL_RGBA, GL_UNSIGNED_BYTE, efb_buffer);
GL_REPORT_ERRORD();
Renderer::SetFramebuffer(0);
Renderer::RestoreGLState();
VertexShaderManager::SetViewportChanged();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0);
Renderer::RestoreGLState();
GL_REPORT_ERRORD();
ConvertToXFB((u32 *)xfb_in_ram, efb_buffer, dstWd, dstHt);
}
void XFB_Draw(u8 *xfb_in_ram, u32 width, u32 height, s32 yOffset)
{
OpenGL_Update(); // just updates the render window position and the backbuffer size
Renderer::SetRenderMode(Renderer::RM_Normal);
// render to the real buffer now
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the backbuffer
glViewport(nXoff, nYoff, nBackbufferWidth, nBackbufferHeight);
Renderer::ResetGLState();
ConvertFromXFB((u32 *)xfb_buffer, xfb_in_ram, width, height);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, xfb_texture);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, xfb_buffer);
TextureMngr::EnableTexRECT(0);
for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i);
GL_REPORT_ERRORD();
glBegin(GL_QUADS);
glTexCoord2f(width, height + yOffset); glVertex2f( 1,-1);
glTexCoord2f(width, 0 + yOffset); glVertex2f( 1, 1);
glTexCoord2f(0, 0 + yOffset); glVertex2f(-1, 1);
glTexCoord2f(0, height + yOffset); glVertex2f(-1,-1);
glEnd();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0);
Renderer::SwapBuffers();
Renderer::RestoreGLState();
GL_REPORT_ERRORD();
}
#endif

View File

@ -18,21 +18,7 @@
#ifndef _XFB_H_ #ifndef _XFB_H_
#define _XFB_H_ #define _XFB_H_
#include "TextureMngr.h"
void XFB_Init();
// write the EFB to the XFB // write the EFB to the XFB
void XFB_Write(u8 *xfb_in_ram, const TRectangle& sourceRc, u32 dstWd, u32 dstHt); void XFB_Write(u8 *xfb_in_ram, const TRectangle& sourceRc, u32 dstWd, u32 dstHt);
// draw the XFB to the screen
void XFB_Draw(u8 *xfb_in_ram, u32 width, u32 height, s32 yOffset);
void XFB_Draw();
void XFB_Shutdown();
int XFB_isInit();
void XFB_SetUpdateArgs(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset);
#endif // _XFB_H_ #endif // _XFB_H_

View File

@ -94,6 +94,8 @@ PLUGIN_GLOBALS* globals = NULL;
// Logging // Logging
int GLScissorX, GLScissorY, GLScissorW, GLScissorH; int GLScissorX, GLScissorY, GLScissorW, GLScissorH;
static bool s_PluginInitialized = false;
#if defined(HAVE_WX) && HAVE_WX #if defined(HAVE_WX) && HAVE_WX
void DllDebugger(HWND _hParent, bool Show) void DllDebugger(HWND _hParent, bool Show)
{ {
@ -359,11 +361,15 @@ void Video_Prepare(void)
GL_REPORT_ERRORD(); GL_REPORT_ERRORD();
VertexLoaderManager::Init(); VertexLoaderManager::Init();
TextureConverter::Init(); TextureConverter::Init();
s_PluginInitialized = true;
INFO_LOG(VIDEO, "Video plugin initialized."); INFO_LOG(VIDEO, "Video plugin initialized.");
} }
void Shutdown(void) void Shutdown(void)
{ {
s_PluginInitialized = false;
Fifo_Shutdown(); Fifo_Shutdown();
PostProcessing::Shutdown(); PostProcessing::Shutdown();
TextureConverter::Shutdown(); TextureConverter::Shutdown();
@ -409,29 +415,46 @@ void Video_AddMessage(const char* pstr, u32 milliseconds)
static volatile struct
{
u8* pXFB;
u32 width;
u32 height;
s32 yOffset;
} tUpdateXFBArgs;
// Run from the CPU thread (from VideoInterface.cpp) for certain homebrew games only // 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(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling)
{ {
if (g_Config.bUseXFB && XFB_isInit()) if (s_PluginInitialized)
{ {
if (scheduling) // from CPU in DC without fifo&CP (some 2D homebrews) if (scheduling) // From CPU in DC mode
{ {
XFB_SetUpdateArgs(_pXFB, _dwWidth, _dwHeight, _dwYOffset); tUpdateXFBArgs.pXFB = _pXFB;
tUpdateXFBArgs.width = _dwWidth;
tUpdateXFBArgs.height = _dwHeight;
tUpdateXFBArgs.yOffset = _dwYOffset;
g_XFBUpdateRequested = TRUE; g_XFBUpdateRequested = TRUE;
} }
else else // From CPU in SC mode or graphics thread in DC mode
{ {
if (_pXFB) // from CPU in SC mode
{
XFB_Draw(_pXFB, _dwWidth, _dwHeight, _dwYOffset);
g_VideoInitialize.pCopiedToXFB();
}
else // from GP in DC without fifo&CP (some 2D homebrews)
{
XFB_Draw();
g_XFBUpdateRequested = FALSE; g_XFBUpdateRequested = FALSE;
if (g_Config.bUseXFB)
{
if (!_pXFB)
// From graphics thread in DC mode
Renderer::DecodeFromXFB(tUpdateXFBArgs.pXFB, tUpdateXFBArgs.width, tUpdateXFBArgs.height, tUpdateXFBArgs.yOffset);
else
// From CPU in SC mode
Renderer::DecodeFromXFB(_pXFB, _dwWidth, _dwHeight, _dwYOffset);
} }
// TODO: Use real XFB source parameters based on VI settings
Renderer::Swap();
g_VideoInitialize.pCopiedToXFB();
} }
} }
} }