diff --git a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp index 27a5dff2de..b381cd2375 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.cpp @@ -29,6 +29,12 @@ GLuint FramebufferManager::m_resolvedDepthTexture; GLuint FramebufferManager::m_xfbFramebuffer; +// reinterpret pixel format +GLuint FramebufferManager::m_pixel_format_vao; +GLuint FramebufferManager::m_pixel_format_vbo; +SHADER FramebufferManager::m_pixel_format_shaders[2]; + + FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples) { m_efbFramebuffer = 0; @@ -65,10 +71,11 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms { // EFB targets will be textures in non-MSAA mode. - GLuint glObj[2]; - glGenTextures(2, glObj); + GLuint glObj[3]; + glGenTextures(3, glObj); m_efbColor = glObj[0]; m_efbDepth = glObj[1]; + m_resolvedColorTexture = glObj[2]; // needed for pixel format convertion glBindTexture(getFbType(), m_efbColor); glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0); @@ -77,6 +84,10 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms glBindTexture(getFbType(), m_efbDepth); glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0); glTexImage2D(getFbType(), 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glBindTexture(getFbType(), m_resolvedColorTexture); + glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0); + glTexImage2D(getFbType(), 0, GL_RGBA8, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Bind target textures to the EFB framebuffer. @@ -164,6 +175,60 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms glClearColor(0.f, 0.f, 0.f, 1.f); glClearDepthf(1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + // reinterpret pixel format + glGenBuffers(1, &m_pixel_format_vbo); + glGenVertexArrays(1, &m_pixel_format_vao); + glBindVertexArray(m_pixel_format_vao); + glBindBuffer(GL_ARRAY_BUFFER, m_pixel_format_vbo); + glEnableVertexAttribArray(SHADER_POSITION_ATTRIB); + glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*2, NULL); + + float vertices[] = { + -1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + 1.0, 1.0, + }; + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + char vs[] = + "ATTRIN vec2 rawpos;\n" + "void main(void) {\n" + " gl_Position = vec4(rawpos,0,1);\n" + "}\n"; + + char ps_rgba6_to_rgb8[] = + "uniform sampler2DRect samp9;\n" + "COLOROUT(ocol0)\n" + "void main()\n" + "{\n" + " ivec4 src6 = ivec4(round(texture2DRect(samp9, gl_FragCoord.xy) * 63.f));\n" + " ivec4 dst8;\n" + " dst8.r = (src6.r << 2) | (src6.g >> 4);\n" + " dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n" + " dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n" + " dst8.a = 255;\n" + " ocol0 = float4(dst8) / 255.f;\n" + "}"; + + char ps_rgb8_to_rgba6[] = + "uniform sampler2DRect samp9;\n" + "COLOROUT(ocol0)\n" + "void main()\n" + "{\n" + " ivec4 src8 = ivec4(round(texture2DRect(samp9, gl_FragCoord.xy) * 255.f));\n" + " ivec4 dst6;\n" + " dst6.r = src8.r >> 2;\n" + " dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n" + " dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n" + " dst6.a = src8.b & 0x3F;\n" + " ocol0 = float4(dst6) / 63.f;\n" + "}"; + + ProgramShaderCache::CompileShader(m_pixel_format_shaders[0], vs, ps_rgb8_to_rgba6); + ProgramShaderCache::CompileShader(m_pixel_format_shaders[1], vs, ps_rgba6_to_rgb8); + } FramebufferManager::~FramebufferManager() @@ -195,6 +260,12 @@ FramebufferManager::~FramebufferManager() glDeleteRenderbuffers(2, glObj); m_efbColor = 0; m_efbDepth = 0; + + // reinterpret pixel format + glDeleteVertexArrays(1, &m_pixel_format_vao); + glDeleteBuffers(1, &m_pixel_format_vbo); + m_pixel_format_shaders[0].Destroy(); + m_pixel_format_shaders[1].Destroy(); } GLuint FramebufferManager::GetEFBColorTexture(const EFBRectangle& sourceRc) @@ -286,6 +357,49 @@ GLuint FramebufferManager::ResolveAndGetDepthTarget(const EFBRectangle &source_r return GetEFBDepthTexture(source_rect); } +void FramebufferManager::ReinterpretPixelData(unsigned int convtype) +{ + g_renderer->ResetAPIState(); + + GLuint src_texture = 0; + + if(m_msaaSamples > 1) + { + // MSAA mode, so resolve first + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer); + glBlitFramebuffer( + 0, 0, m_targetWidth, m_targetHeight, + 0, 0, m_targetWidth, m_targetHeight, + GL_COLOR_BUFFER_BIT, GL_NEAREST + ); + + // Return to EFB. + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_efbFramebuffer); + + src_texture = m_resolvedColorTexture; + } + else + { + // non-MSAA mode, so switch textures + src_texture = m_efbColor; + m_efbColor = m_resolvedColorTexture; + m_resolvedColorTexture = src_texture; + + // also switch them on fbo + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getFbType(), m_efbColor, 0); + } + glViewport(0,0, m_targetWidth, m_targetHeight); + glActiveTexture(GL_TEXTURE0 + 9); + glBindTexture(getFbType(), src_texture); + + m_pixel_format_shaders[convtype ? 1 : 0].Bind(); + glBindVertexArray(m_pixel_format_vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + g_renderer->RestoreAPIState(); +} + XFBSource::~XFBSource() { glDeleteRenderbuffers(1, &renderbuf); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h index 4a8b09722c..0c64a25e58 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/FramebufferManager.h @@ -7,6 +7,7 @@ #include "GLUtil.h" #include "FramebufferManagerBase.h" +#include "ProgramShaderCache.h" #include "Render.h" // On the GameCube, the game sends a request for the graphics processor to @@ -95,6 +96,10 @@ public: // Same as above but for the depth Target. // After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to. static GLuint ResolveAndGetDepthTarget(const EFBRectangle &rect); + + // Convert EFB content on pixel format change. + // convtype=0 -> rgb8->rgba6, convtype=2 -> rgba6->rgb8 + static void ReinterpretPixelData(unsigned int convtype); private: XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height); @@ -111,12 +116,17 @@ private: static GLuint m_efbColor; // Renderbuffer in MSAA mode; Texture otherwise static GLuint m_efbDepth; // Renderbuffer in MSAA mode; Texture otherwise - // Only used in MSAA mode. - static GLuint m_resolvedFramebuffer; + // Only used in MSAA mode and to convert pixel format + static GLuint m_resolvedFramebuffer; // will be hot swapped with m_efbColor on non-msaa pixel format change static GLuint m_resolvedColorTexture; static GLuint m_resolvedDepthTexture; static GLuint m_xfbFramebuffer; // Only used in MSAA mode + + // For pixel format draw + static GLuint m_pixel_format_vbo; + static GLuint m_pixel_format_vao; + static SHADER m_pixel_format_shaders[2]; }; } // namespace OGL diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index 12c22b7277..0bc64a2769 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -547,13 +547,6 @@ Renderer::Renderer() // options while running g_Config.bRunning = true; - if (GL_REPORT_ERROR() != GL_NO_ERROR) - bSuccess = false; - - // Initialize the FramebufferManager - g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height, - s_MSAASamples, s_MSAACoverageSamples); - if (GL_REPORT_ERROR() != GL_NO_ERROR) bSuccess = false; @@ -601,12 +594,12 @@ Renderer::~Renderer() if (scrshotThread.joinable()) scrshotThread.join(); #endif - - delete g_framebuffer_manager; } void Renderer::Shutdown() { + delete g_framebuffer_manager; + g_Config.bRunning = false; UpdateActiveConfig(); @@ -621,6 +614,10 @@ void Renderer::Shutdown() void Renderer::Init() { + // Initialize the FramebufferManager + g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height, + s_MSAASamples, s_MSAACoverageSamples); + s_pfont = new RasterFont(); ProgramShaderCache::CompileShader(s_ShowEFBCopyRegions, @@ -1123,7 +1120,14 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE void Renderer::ReinterpretPixelData(unsigned int convtype) { - // TODO + if (convtype == 0 || convtype == 2) + { + FramebufferManager::ReinterpretPixelData(convtype); + } + else + { + ERROR_LOG(VIDEO, "Trying to reinterpret pixel data with unsupported conversion type %d", convtype); + } } void Renderer::SetBlendMode(bool forceUpdate) diff --git a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp index 1777ecfe22..6b2e92c93a 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp @@ -130,7 +130,7 @@ void InitBackendInfo() g_Config.backend_info.bUseMinimalMipCount = false; g_Config.backend_info.bSupports3DVision = false; //g_Config.backend_info.bSupportsDualSourceBlend = true; // is gpu dependent and must be set in renderer - g_Config.backend_info.bSupportsFormatReinterpretation = false; + g_Config.backend_info.bSupportsFormatReinterpretation = true; g_Config.backend_info.bSupportsPixelLighting = true; //g_Config.backend_info.bSupportsEarlyZ = true; // is gpu dependent and must be set in renderer