diff --git a/Source/Core/VideoCommon/Src/VideoCommon.h b/Source/Core/VideoCommon/Src/VideoCommon.h index 16344e4a9a..3b2cafc22a 100644 --- a/Source/Core/VideoCommon/Src/VideoCommon.h +++ b/Source/Core/VideoCommon/Src/VideoCommon.h @@ -104,6 +104,30 @@ struct TRectangle int GetWidth() const { return right - left; } int GetHeight() const { return bottom - top; } + + void FlipYPosition(int y_height, TRectangle *dest) const + { + int offset = y_height - (bottom - top); + dest->left = left; + dest->top = top + offset; + dest->right = right; + dest->bottom = bottom + offset; + } + + void FlipY(int y_height, TRectangle *dest) const { + dest->left = left; + dest->right = right; + dest->bottom = y_height - bottom; + dest->top = y_height - top; + } + + void Scale(float factor_x, float factor_y, TRectangle *dest) const + { + dest->left = (int)(factor_x * left); + dest->right = (int)(factor_x * right); + dest->top = (int)(factor_y * top); + dest->bottom = (int)(factor_y * bottom); + } }; // Logging diff --git a/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp b/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp index 1992112f73..7adeba7888 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/BPStructs.cpp @@ -451,7 +451,7 @@ void BPWritten(int addr, int changes, int newval) // Clear color Renderer::SetRenderMode(Renderer::RM_Normal); // Clear Z-Buffer target - bool bRestoreZBufferTarget = Renderer::GetFakeZTarget() != 0; + bool bRestoreZBufferTarget = Renderer::UseFakeZTarget(); // Update the view port for clearing the picture glViewport(0, 0, Renderer::GetTargetWidth(), Renderer::GetTargetHeight()); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.cpp b/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.cpp index 3422fa3d09..d663f69c3e 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.cpp @@ -93,6 +93,27 @@ void OpenGL_SetWindowText(const char *text) #endif } +bool OpenGL_CheckFBOStatus() +{ + unsigned int fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + std::string error = "error creating fbo, framebufferstatus is not complete:\n"; + switch (fbo_status) + { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: error += "INCOMPLETE_ATTACHMENT_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "INCOMPLETE_MISSING_ATTACHMENT_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "INCOMPLETE_DIMENSIONS_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "INCOMPLETE_FORMATS_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "INCOMPLETE_DRAW_BUFFER_EXT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "INCOMPLETE_READ_BUFFER_EXT"; break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: error += "UNSUPPORTED_EXT"; break; + } + PanicAlert(error.c_str()); + return false; + } + return true; +} // ======================================================================================= // Draw messages on top of the screen diff --git a/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.h b/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.h index e112789011..9cf97ed90c 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/GLUtil.h @@ -119,14 +119,20 @@ extern GLWindow GLWin; #endif +// Initialization / upkeep +bool OpenGL_Create(SVideoInitialize &_VideoInitialize, int _width, int _height); +void OpenGL_Shutdown(); +void OpenGL_Update(); +bool OpenGL_MakeCurrent(); +void OpenGL_SwapBuffers(); + +// Get status +bool OpenGL_CheckFBOStatus(); u32 OpenGL_GetBackbufferWidth(); u32 OpenGL_GetBackbufferHeight(); -bool OpenGL_Create(SVideoInitialize &_VideoInitialize, int _width, int _height); -bool OpenGL_MakeCurrent(); -void OpenGL_SwapBuffers(); +// Set things void OpenGL_SetWindowText(const char *text); -void OpenGL_Shutdown(); -void OpenGL_Update(); + #endif #endif diff --git a/Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.cpp b/Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.cpp index e065e5c12b..1408f9a74c 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.cpp @@ -109,7 +109,7 @@ FRAGMENTSHADER* PixelShaderCache::GetShader() { DVSTARTPROFILE(); PIXELSHADERUID uid; - u32 zbufrender = (Renderer::GetFakeZTarget() && bpmem.zmode.updateenable) ? 1 : 0; + u32 zbufrender = (Renderer::UseFakeZTarget() && bpmem.zmode.updateenable) ? 1 : 0; u32 zBufRenderToCol0 = Renderer::GetRenderMode() != Renderer::RM_Normal; GetPixelShaderId(uid, PixelShaderManager::GetTextureMask(), zbufrender, zBufRenderToCol0); @@ -127,7 +127,7 @@ FRAGMENTSHADER* PixelShaderCache::GetShader() PSCacheEntry& newentry = pshaders[uid]; const char *code = GeneratePixelShader(PixelShaderManager::GetTextureMask(), - Renderer::GetFakeZTarget() != 0, + Renderer::UseFakeZTarget(), Renderer::GetRenderMode() != Renderer::RM_Normal); #if defined(_DEBUG) || defined(DEBUGFAST) diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index 0b3a2e83bb..d6d4b78241 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -73,7 +73,8 @@ static bool s_bFullscreen = false; static int nZBufferRender = 0; // if > 0, then use zbuffer render, and count down. -static bool MSAA = false; +// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA. +static int s_MSAASamples = 1; // Normal Mode // s_RenderTarget is a texture_rect @@ -93,6 +94,7 @@ static bool MSAA = false; // A framebuffer is a set of render targets: a color and a z buffer. They can be either RenderBuffers or Textures. static GLuint s_uFramebuffer = 0; +static GLuint s_uResolvedFramebuffer = 0; // The size of these should be a (not necessarily even) multiple of the EFB size, 640x528, but isn't. // These are all texture IDs. Bind them as rect arb textures. @@ -107,23 +109,23 @@ static GLuint s_ResolvedDepthTarget = 0; static bool s_bATIDrawBuffers = false; static bool s_bHaveStencilBuffer = false; static bool s_bHaveFramebufferBlit = false; +static bool g_bBlendSeparate = false; +static u32 s_blendMode; static volatile bool s_bScreenshot = false; static Common::CriticalSection s_criticalScreenshot; static std::string s_sScreenshotName; static Renderer::RenderMode s_RenderMode = Renderer::RM_Normal; -bool g_bBlendSeparate = false; int frameCount; static int s_fps = 0; -// These STAY CONSTANT during execution, no matter how much you resize the game window.\ +// These STAY CONSTANT during execution, no matter how much you resize the game window. // TODO: Add functionality to reinit all the render targets when the window is resized. static int s_targetwidth; // Size of render buffer FBO. static int s_targetheight; -static u32 s_blendMode; extern void HandleCgError(CGcontext ctx, CGerror err, void *appdata); @@ -173,8 +175,7 @@ bool Renderer::Init() g_cgcontext = cgCreateContext(); cgGetError(); - cgSetErrorHandler(HandleCgError, NULL); - + cgSetErrorHandler(HandleCgError, NULL); // Look for required extensions. const char *ptoken = (const char*)glGetString(GL_EXTENSIONS); @@ -216,8 +217,12 @@ bool Renderer::Init() ERROR_LOG(VIDEO, "*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\nDoes your video card support OpenGL 2.x?"); bSuccess = false; } - s_bHaveFramebufferBlit = GLEW_EXT_framebuffer_blit ? true : false; - + s_bHaveFramebufferBlit = strstr(ptoken, "GL_EXT_framebuffer_blit") != NULL; + if (!s_bHaveFramebufferBlit) + { + // MSAA ain't gonna work. turn it off if enabled. + s_MSAASamples = 1; + } if (!bSuccess) return false; @@ -246,14 +251,6 @@ bool Renderer::Init() if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers) glDrawBuffers = glDrawBuffersARB; - glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer); - if (s_uFramebuffer == 0) { - ERROR_LOG(VIDEO, "failed to create the renderbuffer\nDoes your video card support OpenGL 2.x?"); - } - - _assert_(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); - // The size of the framebuffer targets should really NOT be the size of the OpenGL viewport. // The EFB is larger than 640x480 - in fact, it's 640x528, give or take a couple of lines. // So the below is wrong. @@ -271,14 +268,19 @@ bool Renderer::Init() if (s_targetheight < EFB_HEIGHT) s_targetheight = EFB_HEIGHT; - if (!MSAA) { + glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer); + if (s_uFramebuffer == 0) { + ERROR_LOG(VIDEO, "failed to create the renderbuffer\nDoes your video card support OpenGL 2.x?"); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); + + if (s_MSAASamples <= 1) { // Create the framebuffer target texture glGenTextures(1, (GLuint *)&s_RenderTarget); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget); // Create our main color render target as a texture rectangle of the desired size. glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); SetDefaultRectTexParams(); - GL_REPORT_ERROR(); GLint nMaxMRT = 0; @@ -318,11 +320,64 @@ bool Renderer::Init() if (s_FakeZTarget == 0) ERROR_LOG(VIDEO, "Disabling ztarget MRT feature (max MRT = %d)\n", nMaxMRT); - } else { - // TODO MSAA rendertarget init } - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + else + { + // MSAA rendertarget init. + // First set up the boring multisampled rendertarget. + glGenRenderbuffersEXT(1, &s_RenderTarget); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_RenderTarget); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, s_MSAASamples, GL_RGBA, s_targetwidth, s_targetheight); + glGenRenderbuffersEXT(1, &s_FakeZTarget); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_FakeZTarget); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, s_MSAASamples, GL_RGBA, s_targetwidth, s_targetheight); + glGenRenderbuffersEXT(1, &s_DepthTarget); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_DepthTarget); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, s_MSAASamples, GL_DEPTH24_STENCIL8_EXT, s_targetwidth, s_targetheight); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + // Attach them to our multisampled FBO. The multisampled FBO is still bound here. + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, s_RenderTarget); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_RENDERBUFFER_EXT, s_FakeZTarget); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_DepthTarget); + OpenGL_CheckFBOStatus(); + + bool bFailed = glGetError() != GL_NO_ERROR || glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT; + if (bFailed) PanicAlert("Incomplete rt"); + + // Create our resolve FBO, and bind it. + glGenFramebuffersEXT(1, (GLuint *)&s_uResolvedFramebuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uResolvedFramebuffer); + + // Generate the resolve targets. + glGenTextures(1, (GLuint *)&s_ResolvedRenderTarget); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ResolvedRenderTarget); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + SetDefaultRectTexParams(); + // Generate the resolve targets. + glGenTextures(1, (GLuint *)&s_ResolvedFakeZTarget); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ResolvedFakeZTarget); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + SetDefaultRectTexParams(); + // Create the real depth/stencil buffer. It's a renderbuffer, not a texture. + glGenRenderbuffersEXT(1, &s_ResolvedDepthTarget); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_ResolvedDepthTarget); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, s_targetwidth, s_targetheight); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // Attach our resolve targets to our resolved FBO. + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ResolvedRenderTarget, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ResolvedFakeZTarget, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_ResolvedDepthTarget); + + OpenGL_CheckFBOStatus(); + + bFailed = glGetError() != GL_NO_ERROR || glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT; + if (bFailed) PanicAlert("Incomplete rt2"); + } + + // glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); nZBufferRender = 0; // Initialize the Z render shutoff countdown. We only render Z if it's desired, to save GPU power. @@ -473,7 +528,7 @@ float Renderer::GetTargetScaleY() void Renderer::SetRenderTarget(GLuint targ) { - 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); } @@ -485,25 +540,64 @@ void Renderer::SetDepthTarget(GLuint targ) void Renderer::SetFramebuffer(GLuint fb) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, - fb != 0 ? fb : s_uFramebuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb != 0 ? fb : s_uFramebuffer); } GLuint Renderer::ResolveAndGetRenderTarget(const TRectangle &source_rect) { - return s_RenderTarget; + if (s_MSAASamples > 1) + { + // Flip the rectangle + TRectangle flipped_rect; + source_rect.FlipYPosition(GetTargetHeight(), &flipped_rect); + + // Do the resolve. + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, s_uResolvedFramebuffer); + glBlitFramebufferEXT(flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom, + flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); + + // Return the resolved target. + return s_ResolvedRenderTarget; + } + else + { + return s_RenderTarget; + } } GLuint Renderer::ResolveAndGetFakeZTarget(const TRectangle &source_rect) { // This logic should be moved elsewhere. - return s_FakeZTarget; + if (s_MSAASamples > 1) + { + // Flip the rectangle + TRectangle flipped_rect; + source_rect.FlipYPosition(GetTargetHeight(), &flipped_rect); + + // Do the resolve. We resolve both color channels, not very necessary. + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, s_uResolvedFramebuffer); + glBlitFramebufferEXT(flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom, + flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); + + // Return the resolved target. + return s_ResolvedFakeZTarget; + } + else + { + return s_FakeZTarget; + } } -GLuint Renderer::GetFakeZTarget() +bool Renderer::UseFakeZTarget() { // This logic should be moved elsewhere. - return nZBufferRender > 0 ? s_FakeZTarget : 0; + return nZBufferRender > 0; } void Renderer::ResetGLState() @@ -768,7 +862,7 @@ void Renderer::SetRenderMode(RenderMode mode) } else if (s_RenderMode == RM_Normal) { // setup buffers - _assert_(GetFakeZTarget() && bpmem.zmode.updateenable); + _assert_(UseFakeZTarget() && bpmem.zmode.updateenable); if (mode == RM_ZBufferAlpha) { glEnable(GL_STENCIL_TEST); glClearStencil(0); @@ -782,7 +876,7 @@ void Renderer::SetRenderMode(RenderMode mode) GL_REPORT_ERRORD(); } else { - _assert_(GetFakeZTarget()); + _assert_(UseFakeZTarget()); _assert_(s_bHaveStencilBuffer); if (mode == RM_ZBufferOnly) { @@ -897,72 +991,87 @@ void Renderer::Swap(const TRectangle& rc) TRectangle back_rc; ComputeBackbufferRectangle(&back_rc); - - // Disable all other stages. - for (int i = 1; i < 8; ++i) - TextureMngr::DisableStage(i); - - // Update GLViewPort - glViewport(back_rc.left, back_rc.top, - back_rc.right - back_rc.left, back_rc.bottom - back_rc.top); - - GL_REPORT_ERRORD(); - - // Copy the framebuffer to screen. - // TODO: Use glBlitFramebufferEXT. - -#if 0 - // Use framebuffer blit to stretch screen. No messing around with annoying glBegin and viewports. - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); - glBlitFramebufferEXT(0, GetTargetHeight() - (rc.bottom - rc.top), rc.GetWidth(), rc.GetHeight(), - back_rc.left, back_rc.top, back_rc.GetWidth(), back_rc.GetHeight(), - GL_COLOR_BUFFER_BIT, GL_LINEAR); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer - //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer); -#else - // Render to the real buffer now. - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer - - // Texture map s_RenderTargets[s_curtarget] onto the main buffer - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget); - // 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); - TextureMngr::EnableTexRECT(0); - float u_max; float v_min = 0.f; float v_max; - if (g_Config.bAutoScale) { + if (g_Config.bAutoScale) + { u_max = (rc.right - rc.left); v_min = (float)GetTargetHeight() - (rc.bottom - rc.top); v_max = (float)GetTargetHeight(); - } else { - u_max = (float)GetTargetWidth(); - v_max = (float)GetTargetHeight(); // TODO - when we change the target height to 528, this will have to change. } - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glBegin(GL_QUADS); - glTexCoord2f(0, v_min); glVertex2f(-1, -1); - glTexCoord2f(0, v_max); glVertex2f(-1, 1); - glTexCoord2f(u_max, v_max); glVertex2f( 1, 1); - glTexCoord2f(u_max, v_min); glVertex2f( 1, -1); - glEnd(); -#endif + else + { + u_max = (float)GetTargetWidth(); + v_max = (float)GetTargetHeight(); + } - // Restore filtering. - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + if (s_bHaveFramebufferBlit) + { + // 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); + } + 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); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer, we'll draw debug text on top + } + 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) + TextureMngr::DisableStage(i); - // Wireframe - if (g_Config.bWireFrame) - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + // Update GLViewPort + glViewport(back_rc.left, back_rc.top, + back_rc.right - back_rc.left, back_rc.bottom - back_rc.top); + + GL_REPORT_ERRORD(); + + // Copy the framebuffer to screen. + // TODO: Use glBlitFramebufferEXT. + // Render to the real buffer now. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer + + // Texture map s_RenderTargets[s_curtarget] onto the main buffer + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget); + // 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); + TextureMngr::EnableTexRECT(0); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glBegin(GL_QUADS); + glTexCoord2f(0, v_min); glVertex2f(-1, -1); + glTexCoord2f(0, v_max); glVertex2f(-1, 1); + glTexCoord2f(u_max, v_max); glVertex2f( 1, 1); + glTexCoord2f(u_max, v_min); glVertex2f( 1, -1); + glEnd(); + + // Restore filtering. + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + // Wireframe + if (g_Config.bWireFrame) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + TextureMngr::DisableStage(0); + // End of non-framebuffer_blit workaround. + } - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); - TextureMngr::DisableStage(0); - // ------------------------------------- // Take screenshot, if requested if (s_bScreenshot) { s_criticalScreenshot.Enter(); @@ -995,6 +1104,8 @@ void Renderer::Swap(const TRectangle& rc) void Renderer::DrawDebugText() { + // Reset viewport for drawing text + glViewport(0, 0, OpenGL_GetBackbufferWidth(), OpenGL_GetBackbufferHeight()); // Draw various messages on the screen, like FPS, statistics, etc. char debugtext_buffer[8192]; char *p = debugtext_buffer; diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.h b/Source/Plugins/Plugin_VideoOGL/Src/Render.h index b1ac48c8d2..00a4ff2b35 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.h @@ -88,11 +88,13 @@ public: // If in MSAA mode, this will perform a resolve of the specified rectangle, and return the resolve target as a texture ID. // Thus, this call may be expensive. Don't repeat it unnecessarily. // If not in MSAA mode, will just return the render target texture ID. + // After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to. static GLuint ResolveAndGetRenderTarget(const TRectangle &rect); // Same as above but for the FakeZ Target. + // After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to. static GLuint ResolveAndGetFakeZTarget(const TRectangle &rect); - static GLuint GetFakeZTarget(); // This is used by some functions to check for Z target existence. Should be changed to a bool. + static bool UseFakeZTarget(); // This is used by some functions to check for Z target existence. // Random utilities static void RenderText(const char* pstr, int left, int top, u32 color); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp index 52c18e6c0a..2acf5676ac 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp @@ -99,7 +99,7 @@ void CreateYuyvToRgbProgram() } } -FRAGMENTSHADER& GetOrCreateEncodingShader(u32 format) +FRAGMENTSHADER &GetOrCreateEncodingShader(u32 format) { if (format > NUM_ENCODING_PROGRAMS) { @@ -245,11 +245,17 @@ void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyf if (texconv_shader.glprogid == 0) return; - u8* ptr = Memory_GetPtr(address); + u8 *dest_ptr = Memory_GetPtr(address); u32 source_texture = bFromZBuffer ? Renderer::ResolveAndGetFakeZTarget(source) : Renderer::ResolveAndGetRenderTarget(source); - s32 width = source.right - source.left; - s32 height = source.bottom - source.top; + int width = source.right - source.left; + int height = source.bottom - source.top; + + int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format); + + // Invalidate any existing texture covering this memory range. + // TODO - don't delete the texture if it already exists, just replace the contents. + TextureMngr::InvalidateRange(address, size_in_bytes); if (bScaleByHalf) { @@ -280,7 +286,7 @@ void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyf scaledSource.left = 0; scaledSource.right = expandedWidth / samples; - EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, ptr, expandedWidth / samples, expandedHeight, bScaleByHalf); + EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, dest_ptr, expandedWidth / samples, expandedHeight, bScaleByHalf); if (bFromZBuffer) Renderer::SetZBufferRender(); // notify for future settings diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.cpp b/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.cpp index d4aa737fab..0d05d05100 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.cpp @@ -93,6 +93,15 @@ bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int he return SaveTGA(filename, width, height, &data[0]); } +bool TextureMngr::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size) +{ + if (addr + size_in_bytes < range_address) + return false; + if (addr >= range_address + range_size) + return false; + return true; +} + void TextureMngr::TCacheEntry::SetTextureParameters(TexMode0 &newmode) { mode = newmode; @@ -179,6 +188,12 @@ void TextureMngr::Shutdown() temp = NULL; } +#ifdef _WIN32 +#define ERASE_THROUGH_ITERATOR(container, iterator) iterator = container.erase(iterator) +#else +#define ERASE_THROUGH_ITERATOR(container, iterator) container.erase(iterator++) +#endif + void TextureMngr::ProgressiveCleanup() { TexCache::iterator iter = textures.begin(); @@ -188,19 +203,11 @@ void TextureMngr::ProgressiveCleanup() { if (!iter->second.isRenderTarget) { iter->second.Destroy(false); -#ifdef _WIN32 - iter = textures.erase(iter); -#else - textures.erase(iter++); -#endif + ERASE_THROUGH_ITERATOR(textures, iter); } else { iter->second.Destroy(false); -#ifdef _WIN32 - iter = textures.erase(iter); -#else - textures.erase(iter++); -#endif + ERASE_THROUGH_ITERATOR(textures, iter); } } else @@ -211,17 +218,28 @@ void TextureMngr::ProgressiveCleanup() while (itdepth != mapDepthTargets.end()) { if (frameCount > 20 + itdepth->second.framecount) { -#ifdef _WIN32 - itdepth = mapDepthTargets.erase(itdepth); -#else - mapDepthTargets.erase(itdepth++); -#endif + ERASE_THROUGH_ITERATOR(mapDepthTargets, itdepth); } else ++itdepth; } } -TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt) +void TextureMngr::InvalidateRange(u32 start_address, u32 size) { + TexCache::iterator iter = textures.begin(); + while (iter != textures.end()) + { + if (iter->second.IntersectsMemoryRange(start_address, size)) + { + iter->second.Destroy(false); + ERASE_THROUGH_ITERATOR(textures, iter); + } + else { + ++iter; + } + } +} + +TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width, int height, int tex_format, int tlutaddr, int tlutfmt) { /* notes (about "UNsafe texture cache"): * Have to be removed soon. @@ -232,7 +250,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width /* notes (about "safe texture cache"): * Metroids text issue (character table): * Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs). - * That's why we have to hash the TLUT too for TLUT format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2). + * That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2). * And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but * have to be a real unique ID. * DONE but not satifiying yet -> may break copyEFBToTexture sometimes. @@ -253,26 +271,26 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width TexMode0 &tm0 = bpmem.tex[texstage > 3].texMode0[texstage & 3]; u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address); - int bs = TexDecoder_GetBlockWidthInTexels(format) - 1; + int bs = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; int expandedWidth = (width + bs) & (~bs); u32 hash_value; u32 texID = address; if (g_Config.bSafeTextureCache) { - hash_value = TexDecoder_GetSafeTextureHash(ptr, expandedWidth, height, format, 0); // remove last arg - if ((format == GX_TF_C4) || (format == GX_TF_C8) || (format == GX_TF_C14X2)) + hash_value = TexDecoder_GetSafeTextureHash(ptr, expandedWidth, height, tex_format, 0); // remove last arg + if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) { // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. - //texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], TexDecoder_GetPaletteSize(format)); + //texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format)); // This trick (to change the texID depending on the TLUT addr) is a trick to get around // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of // each other stored in a single texture, and uses the palette to make different characters // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, // we must make sure that texture with different tluts get different IDs. - texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], (format == GX_TF_C4) ? 32 : 128); + texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], (tex_format == GX_TF_C4) ? 32 : 128); //DebugLog("addr: %08x | texID: %08x | texHash: %08x", address, texID, hash_value); } } @@ -295,7 +313,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width if (entry.mode.hex != tm0.hex) entry.SetTextureParameters(tm0); //DebugLog("%cC addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_Config.bSafeTextureCache ? 'S' : 'U' - // , address, format, entry.hash, width, height); + // , address, tex_format, entry.hash, width, height); return &entry; } else @@ -303,7 +321,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width // Let's reload the new texture data into the same texture, // instead of destroying it and having to create a new one. // Might speed up movie playback very, very slightly. - if (width == entry.w && height == entry.h && format == entry.fmt) + if (width == entry.w && height == entry.h && tex_format == entry.fmt) { glBindTexture(entry.isNonPow2 ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D, entry.texture); if (entry.mode.hex != tm0.hex) @@ -318,7 +336,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width } } - PC_TexFormat dfmt = TexDecoder_Decode(temp, ptr, expandedWidth, height, format, tlutaddr, tlutfmt); + PC_TexFormat dfmt = TexDecoder_Decode(temp, ptr, expandedWidth, height, tex_format, tlutaddr, tlutfmt); //Make an entry in the table TCacheEntry& entry = textures[texID]; @@ -335,10 +353,11 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width ((u32 *)ptr)[entry.hashoffset] = entry.hash; } //DebugLog("%c addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_Config.bSafeTextureCache ? 'S' : 'U' - // , address, format, entry.hash, width, height); + // , address, tex_format, entry.hash, width, height); entry.addr = address; + entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, tex_format); entry.isRenderTarget = false; entry.isNonPow2 = ((width & (width - 1)) || (height & (height - 1))); @@ -354,7 +373,8 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width int gl_format; int gl_iformat; int gl_type; - switch (dfmt) { + switch (dfmt) + { default: case PC_TEX_FMT_NONE: PanicAlert("Invalid PC texture format %i", dfmt); @@ -397,14 +417,14 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width entry.frameCount = frameCount; entry.w = width; entry.h = height; - entry.fmt = format; + entry.fmt = tex_format; entry.SetTextureParameters(tm0); if (g_Config.bDumpTextures) // dump texture to file { static int counter = 0; char szTemp[MAX_PATH]; - sprintf(szTemp, "%s/txt_%04i_%i.tga", FULL_DUMP_TEXTURES_DIR, counter++, format); + sprintf(szTemp, "%s/txt_%04i_%i.tga", FULL_DUMP_TEXTURES_DIR, counter++, tex_format); SaveTexture(szTemp,target, entry.texture, width, height); } @@ -616,7 +636,16 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool // GL_REPORT_ERRORD(); // return; // } - + + TRectangle scaled_rect; + source_rect.Scale(Renderer::GetTargetScaleX(), Renderer::GetTargetScaleY(), &scaled_rect); + TRectangle flipped_rect; + scaled_rect.FlipY(Renderer::GetTargetHeight(), &flipped_rect); + + // Make sure to resolve anything we need to read from. + // TODO - it seems that it sometimes doesn't resolve the entire area we are interested in. See shadows in Burnout 2. + GLuint read_texture = bFromZBuffer ? Renderer::ResolveAndGetFakeZTarget(scaled_rect) : Renderer::ResolveAndGetRenderTarget(scaled_rect); + Renderer::SetRenderMode(Renderer::RM_Normal); // set back to normal GL_REPORT_ERRORD(); @@ -633,7 +662,6 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool // create and attach the render target std::map::iterator itdepth = mapDepthTargets.find((h << 16) | w); - if (itdepth == mapDepthTargets.end()) { DEPTHTARGET& depth = mapDepthTargets[(h << 16) | w]; @@ -657,10 +685,10 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, (bFromZBuffer ? Renderer::ResolveAndGetFakeZTarget(source_rect) : Renderer::ResolveAndGetRenderTarget(source_rect))); - TextureMngr::EnableTexRECT(0); - + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture); + glViewport(0, 0, w, h); glEnable(GL_FRAGMENT_PROGRAM_ARB); @@ -668,15 +696,11 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation GL_REPORT_ERRORD(); - // Get Target X, Y - float MValueX = Renderer::GetTargetScaleX(); - float MValueY = Renderer::GetTargetScaleY(); - glBegin(GL_QUADS); - glTexCoord2f((float)source_rect.left * MValueX, Renderer::GetTargetHeight()-(float)source_rect.bottom * MValueY); glVertex2f(-1, 1); - glTexCoord2f((float)source_rect.left * MValueX, Renderer::GetTargetHeight()-(float)source_rect.top * MValueY); glVertex2f(-1, -1); - glTexCoord2f((float)source_rect.right * MValueX, Renderer::GetTargetHeight()-(float)source_rect.top * MValueY); glVertex2f( 1, -1); - glTexCoord2f((float)source_rect.right * MValueX, Renderer::GetTargetHeight()-(float)source_rect.bottom * MValueY); glVertex2f( 1, 1); + glTexCoord2f(flipped_rect.left, flipped_rect.bottom); glVertex2f(-1, 1); + glTexCoord2f(flipped_rect.left, flipped_rect.top ); glVertex2f(-1, -1); + glTexCoord2f(flipped_rect.right, flipped_rect.top ); glVertex2f( 1, -1); + glTexCoord2f(flipped_rect.right, flipped_rect.bottom); glVertex2f( 1, 1); glEnd(); GL_REPORT_ERRORD(); @@ -691,7 +715,7 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool GL_REPORT_ERRORD(); - if(g_Config.bDumpEFBTarget) + if (g_Config.bDumpEFBTarget) { static int count = 0; SaveTexture(StringFromFormat("%s/efb_frame_%i.tga", FULL_DUMP_TEXTURES_DIR, count++).c_str(), GL_TEXTURE_RECTANGLE_ARB, entry.texture, entry.w, entry.h); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.h b/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.h index 5cd2637fca..e687e1b132 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureMngr.h @@ -29,10 +29,11 @@ class TextureMngr public: struct TCacheEntry { - TCacheEntry() : texture(0), addr(0), hash(0), w(0), h(0), isRenderTarget(false), isUpsideDown(false), isNonPow2(true), bHaveMipMaps(false) { mode.hex = 0xFCFCFCFC; } + TCacheEntry() : texture(0), addr(0), size_in_bytes(0), hash(0), w(0), h(0), isRenderTarget(false), isUpsideDown(false), isNonPow2(true), bHaveMipMaps(false) { mode.hex = 0xFCFCFCFC; } GLuint texture; u32 addr; + u32 size_in_bytes; u32 hash; u32 paletteHash; u32 hashoffset; @@ -40,7 +41,7 @@ public: TexMode0 mode; // current filter and clamp modes that texture is set to int frameCount; - int w,h,fmt; + int w, h, fmt; bool isRenderTarget; // if render texture, then rendertex is filled with the direct copy of the render target // later conversions would have to convert properly from rendertexfmt to texfmt @@ -51,6 +52,7 @@ public: void SetTextureParameters(TexMode0& newmode); void Destroy(bool shutdown); void ConvertFromRenderTarget(u32 taddr, int twidth, int theight, int tformat, int tlutaddr, int tlutfmt); + bool IntersectsMemoryRange(u32 range_address, u32 range_size); }; struct DEPTHTARGET @@ -73,6 +75,8 @@ public: static void ProgressiveCleanup(); static void Shutdown(); static void Invalidate(bool shutdown); + static void InvalidateRange(u32 start_address, u32 size); + static TCacheEntry* Load(int texstage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt); static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, bool bScaleByHalf, const TRectangle &source); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp index 42d014e744..6cea9e6828 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp @@ -242,7 +242,7 @@ void Flush() VERTEXSHADER* vs = VertexShaderCache::GetShader(g_nativeVertexFmt->m_components); bool bRestoreBuffers = false; - if (Renderer::GetFakeZTarget()) { + if (Renderer::UseFakeZTarget()) { if (bpmem.zmode.updateenable) { if (!bpmem.blendmode.colorupdate) { Renderer::SetRenderMode(bpmem.blendmode.alphaupdate ? Renderer::RM_ZBufferAlpha : Renderer::RM_ZBufferOnly); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.cpp b/Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.cpp index da20a54397..f940ca6b9b 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.cpp @@ -72,7 +72,7 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components) { DVSTARTPROFILE(); VERTEXSHADERUID uid; - u32 zbufrender = (bpmem.ztex2.op == ZTEXTURE_ADD) || Renderer::GetFakeZTarget() != 0; + u32 zbufrender = (bpmem.ztex2.op == ZTEXTURE_ADD) || Renderer::UseFakeZTarget(); GetVertexShaderId(uid, components, zbufrender); VSCache::iterator iter = vshaders.find(uid); @@ -87,7 +87,7 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components) } VSCacheEntry& entry = vshaders[uid]; - const char *code = GenerateVertexShader(components, Renderer::GetFakeZTarget() != 0); + const char *code = GenerateVertexShader(components, Renderer::UseFakeZTarget()); #if defined(_DEBUG) || defined(DEBUGFAST) if (g_Config.iLog & CONF_SAVESHADERS && code) {