From 3b354a009684cda37ba8670940ed162f68746128 Mon Sep 17 00:00:00 2001 From: rogerman Date: Fri, 4 Aug 2017 16:54:51 -0700 Subject: [PATCH] OpenGL Renderer: Fix longstanding blending bug where zero-alpha destination fragments were being blended with instead of being overwritten. This fixes certain coloring bugs in many games. (Does not currently work with MSAA.) --- desmume/src/OGLRender.cpp | 383 ++++++++++++++++++++++++++++------ desmume/src/OGLRender.h | 60 +++--- desmume/src/OGLRender_3_2.cpp | 304 ++++++++++++++++++++------- desmume/src/OGLRender_3_2.h | 8 +- desmume/src/rasterize.cpp | 4 +- desmume/src/rasterize.h | 4 +- desmume/src/render3D.cpp | 2 +- desmume/src/render3D.h | 2 +- 8 files changed, 592 insertions(+), 175 deletions(-) diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp index da2315c89..292ceaa2f 100755 --- a/desmume/src/OGLRender.cpp +++ b/desmume/src/OGLRender.cpp @@ -265,7 +265,7 @@ static void OGLLoadEntryPoints_Legacy() } // Vertex Shader GLSL 1.00 -static const char *vertexShader_100 = {"\ +static const char *GeometryVtxShader_100 = {"\ attribute vec4 inPosition; \n\ attribute vec2 inTexCoord0; \n\ attribute vec3 inColor; \n\ @@ -291,7 +291,7 @@ static const char *vertexShader_100 = {"\ "}; // Fragment Shader GLSL 1.00 -static const char *fragmentShader_100 = {"\ +static const char *GeometryFragShader_100 = {"\ varying vec4 vtxPosition;\n\ varying vec2 vtxTexCoord;\n\ varying vec4 vtxColor;\n\ @@ -388,6 +388,35 @@ static const char *fragmentShader_100 = {"\ }\n\ "}; +// Vertex shader for determining which pixels have a zero alpha, GLSL 1.00 +static const char *GeometryZeroDstAlphaPixelMaskVtxShader_100 = {"\ + attribute vec2 inPosition;\n\ + attribute vec2 inTexCoord0;\n\ + varying vec2 texCoord;\n\ + \n\ + void main()\n\ + {\n\ + texCoord = inTexCoord0;\n\ + gl_Position = vec4(inPosition, 0.0, 1.0);\n\ + }\n\ +"}; + +// Fragment shader for determining which pixels have a zero alpha, GLSL 1.00 +static const char *GeometryZeroDstAlphaPixelMaskFragShader_100 = {"\ + varying vec2 texCoord;\n\ + uniform sampler2D texInFragColor;\n\ + \n\ + void main()\n\ + {\n\ + vec4 inFragColor = texture2D(texInFragColor, texCoord);\n\ + \n\ + if (inFragColor.a <= 0.001)\n\ + {\n\ + discard;\n\ + }\n\ + }\n\ +"}; + // Vertex shader for determining which pixels have a zero alpha, GLSL 1.00 static const char *ZeroAlphaPixelMaskVtxShader_100 = {"\ attribute vec2 inPosition;\n\ @@ -1037,11 +1066,13 @@ OpenGLRenderer::OpenGLRenderer() ref->fboMSIntermediateRenderID = 0; ref->fboPostprocessID = 0; ref->selectedRenderingFBO = 0; + ref->texGDepthStencilAlphaID = 0; ref->texFinalColorID = 0; _mappedFramebuffer = NULL; _workingTextureUnpackBuffer = (FragmentColor *)malloc_alignedCacheLine(1024 * 1024 * sizeof(FragmentColor)); _pixelReadNeedsFinish = false; + _needsZeroDstAlphaPass = true; _currentPolyIndex = 0; _lastTextureDrawTarget = OGLTextureUnitID_GColor; } @@ -1743,35 +1774,19 @@ Render3DError OpenGLRenderer_1_2::InitExtensions() if ( (maxDrawBuffersOGL >= 4) && (maxShaderTexUnitsOGL >= 8) ) { - std::string geometryVtxShaderString; - std::string geometryFragShaderString; - - error = this->LoadGeometryShaders(geometryVtxShaderString, geometryFragShaderString); + error = this->InitGeometryProgram(GeometryVtxShader_100, GeometryFragShader_100, + GeometryZeroDstAlphaPixelMaskVtxShader_100, GeometryZeroDstAlphaPixelMaskFragShader_100); if (error == OGLERROR_NOERR) { - error = this->InitGeometryProgram(geometryVtxShaderString.c_str(), geometryFragShaderString.c_str()); - if (error == OGLERROR_NOERR) - { - std::string zeroAlphaPixelMaskVtxShaderString = std::string(ZeroAlphaPixelMaskVtxShader_100); - std::string zeroAlphaPixelMaskFragShaderString = std::string(ZeroAlphaPixelMaskFragShader_100); - std::string edgeMarkVtxShaderString = std::string(EdgeMarkVtxShader_100); - std::string edgeMarkFragShaderString = std::string(EdgeMarkFragShader_100); - std::string fogVtxShaderString = std::string(FogVtxShader_100); - std::string fogFragShaderString = std::string(FogFragShader_100); - std::string framebufferOutputVtxShaderString = std::string(FramebufferOutputVtxShader_100); - std::string framebufferOutputRGBA6665FragShaderString = std::string(FramebufferOutputRGBA6665FragShader_100); - std::string framebufferOutputRGBA8888FragShaderString = std::string(FramebufferOutputRGBA8888FragShader_100); - - error = this->InitPostprocessingPrograms(zeroAlphaPixelMaskVtxShaderString.c_str(), - zeroAlphaPixelMaskFragShaderString.c_str(), - edgeMarkVtxShaderString.c_str(), - edgeMarkFragShaderString.c_str(), - fogVtxShaderString.c_str(), - fogFragShaderString.c_str(), - framebufferOutputVtxShaderString.c_str(), - framebufferOutputRGBA6665FragShaderString.c_str(), - framebufferOutputRGBA8888FragShaderString.c_str()); - } + error = this->InitPostprocessingPrograms(ZeroAlphaPixelMaskVtxShader_100, + ZeroAlphaPixelMaskFragShader_100, + EdgeMarkVtxShader_100, + EdgeMarkFragShader_100, + FogVtxShader_100, + FogFragShader_100, + FramebufferOutputVtxShader_100, + FramebufferOutputRGBA6665FragShader_100, + FramebufferOutputRGBA8888FragShader_100); } if (error != OGLERROR_NOERR) @@ -1892,7 +1907,12 @@ Render3DError OpenGLRenderer_1_2::InitExtensions() if (maxSamplesOGL >= 2) { - error = this->CreateMultisampledFBO(); + if (maxSamplesOGL > OGLRENDER_MAX_MULTISAMPLES) + { + maxSamplesOGL = OGLRENDER_MAX_MULTISAMPLES; + } + + error = this->CreateMultisampledFBO(maxSamplesOGL); if (error != OGLERROR_NOERR) { this->isMultisampledFBOSupported = false; @@ -1997,17 +2017,6 @@ void OpenGLRenderer_1_2::DestroyPBOs() this->isPBOSupported = false; } -Render3DError OpenGLRenderer_1_2::LoadGeometryShaders(std::string &outVertexShaderProgram, std::string &outFragmentShaderProgram) -{ - outVertexShaderProgram.clear(); - outFragmentShaderProgram.clear(); - - outVertexShaderProgram += std::string(vertexShader_100); - outFragmentShaderProgram += std::string(fragmentShader_100); - - return OGLERROR_NOERR; -} - Render3DError OpenGLRenderer_1_2::InitGeometryProgramBindings() { OGLRenderRef &OGLRef = *this->ref; @@ -2054,7 +2063,29 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgramShaderLocations() return OGLERROR_NOERR; } -Render3DError OpenGLRenderer_1_2::InitGeometryProgram(const char *geometryVtxShaderCString, const char *geometryFragShaderCString) +Render3DError OpenGLRenderer_1_2::InitGeometryZeroDstAlphaProgramBindings() +{ + OGLRenderRef &OGLRef = *this->ref; + + glBindAttribLocation(OGLRef.programGeometryZeroDstAlphaID, OGLVertexAttributeID_Position, "inPosition"); + glBindAttribLocation(OGLRef.programGeometryZeroDstAlphaID, OGLVertexAttributeID_TexCoord0, "inTexCoord0"); + + return OGLERROR_NOERR; +} + +Render3DError OpenGLRenderer_1_2::InitGeometryZeroDstAlphaProgramShaderLocations() +{ + OGLRenderRef &OGLRef = *this->ref; + + glUseProgram(OGLRef.programGeometryZeroDstAlphaID); + const GLint uniformTexGColor = glGetUniformLocation(OGLRef.programGeometryZeroDstAlphaID, "texInFragColor"); + glUniform1i(uniformTexGColor, OGLTextureUnitID_GColor); + + return OGLERROR_NOERR; +} + +Render3DError OpenGLRenderer_1_2::InitGeometryProgram(const char *geometryVtxShaderCString, const char *geometryFragShaderCString, + const char *geometryAlphaVtxShaderCString, const char *geometryAlphaFragShaderCString) { OGLRenderRef &OGLRef = *this->ref; @@ -2124,6 +2155,75 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgram(const char *geometryVtxSha this->InitGeometryProgramShaderLocations(); + // ------------------------------------------ + + OGLRef.vtxShaderGeometryZeroDstAlphaID = glCreateShader(GL_VERTEX_SHADER); + if(!OGLRef.vtxShaderGeometryZeroDstAlphaID) + { + INFO("OpenGL: Failed to create the vertex shader. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); + return OGLERROR_SHADER_CREATE_ERROR; + } + + const char *vertexAlphaShaderProgramChar = geometryAlphaVtxShaderCString; + glShaderSource(OGLRef.vtxShaderGeometryZeroDstAlphaID, 1, (const GLchar **)&vertexAlphaShaderProgramChar, NULL); + glCompileShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + if (!this->ValidateShaderCompile(OGLRef.vtxShaderGeometryZeroDstAlphaID)) + { + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + INFO("OpenGL: Failed to compile the vertex shader. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); + return OGLERROR_SHADER_CREATE_ERROR; + } + + OGLRef.fragShaderGeometryZeroDstAlphaID = glCreateShader(GL_FRAGMENT_SHADER); + if(!OGLRef.fragShaderGeometryZeroDstAlphaID) + { + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + INFO("OpenGL: Failed to create the fragment shader. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); + return OGLERROR_SHADER_CREATE_ERROR; + } + + const char *fragmentAlphaShaderProgramChar = geometryAlphaFragShaderCString; + glShaderSource(OGLRef.fragShaderGeometryZeroDstAlphaID, 1, (const GLchar **)&fragmentAlphaShaderProgramChar, NULL); + glCompileShader(OGLRef.fragShaderGeometryZeroDstAlphaID); + if (!this->ValidateShaderCompile(OGLRef.fragShaderGeometryZeroDstAlphaID)) + { + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.fragShaderGeometryZeroDstAlphaID); + INFO("OpenGL: Failed to compile the fragment shader. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); + return OGLERROR_SHADER_CREATE_ERROR; + } + + OGLRef.programGeometryZeroDstAlphaID = glCreateProgram(); + if(!OGLRef.programGeometryZeroDstAlphaID) + { + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.fragShaderGeometryZeroDstAlphaID); + INFO("OpenGL: Failed to create the shader program. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); + return OGLERROR_SHADER_CREATE_ERROR; + } + + glAttachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.vtxShaderGeometryZeroDstAlphaID); + glAttachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.fragShaderGeometryZeroDstAlphaID); + + this->InitGeometryZeroDstAlphaProgramBindings(); + + glLinkProgram(OGLRef.programGeometryZeroDstAlphaID); + if (!this->ValidateShaderProgramLink(OGLRef.programGeometryZeroDstAlphaID)) + { + glDetachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDetachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.fragShaderGeometryZeroDstAlphaID); + glDeleteProgram(OGLRef.programGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.fragShaderGeometryZeroDstAlphaID); + INFO("OpenGL: Failed to link the shader program. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); + return OGLERROR_SHADER_CREATE_ERROR; + } + + glValidateProgram(OGLRef.programGeometryZeroDstAlphaID); + this->InitGeometryZeroDstAlphaProgramShaderLocations(); + + // ------------------------------------------ + INFO("OpenGL: Successfully created shaders.\n"); this->CreateToonTable(); @@ -2148,6 +2248,12 @@ void OpenGLRenderer_1_2::DestroyGeometryProgram() glDeleteShader(OGLRef.vertexGeometryShaderID); glDeleteShader(OGLRef.fragmentGeometryShaderID); + glDetachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDetachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.fragShaderGeometryZeroDstAlphaID); + glDeleteProgram(OGLRef.programGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.fragShaderGeometryZeroDstAlphaID); + this->DestroyToonTable(); this->isShaderSupported = false; @@ -2217,6 +2323,7 @@ Render3DError OpenGLRenderer_1_2::CreateFBOs() glGenTextures(1, &OGLRef.texGFogAttrID); glGenTextures(1, &OGLRef.texGPolyID); glGenTextures(1, &OGLRef.texGDepthStencilID); + glGenTextures(1, &OGLRef.texGDepthStencilAlphaID); glGenTextures(1, &OGLRef.texZeroAlphaPixelMaskID); glActiveTextureARB(GL_TEXTURE0_ARB + OGLTextureUnitID_DepthStencil); @@ -2262,6 +2369,17 @@ Render3DError OpenGLRenderer_1_2::CreateFBOs() glActiveTextureARB(GL_TEXTURE0_ARB); + if (this->isShaderSupported && this->isFBOSupported) + { + glBindTexture(GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT, this->_framebufferWidth, this->_framebufferHeight, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, NULL); + } + glBindTexture(GL_TEXTURE_2D, OGLRef.texCIColorID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -2296,6 +2414,7 @@ Render3DError OpenGLRenderer_1_2::CreateFBOs() // Set up FBOs glGenFramebuffersEXT(1, &OGLRef.fboClearImageID); glGenFramebuffersEXT(1, &OGLRef.fboRenderID); + glGenFramebuffersEXT(1, &OGLRef.fboRenderAlphaID); glGenFramebuffersEXT(1, &OGLRef.fboPostprocessID); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.fboClearImageID); @@ -2350,6 +2469,27 @@ Render3DError OpenGLRenderer_1_2::CreateFBOs() glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + if (this->isShaderSupported && this->isVBOSupported) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.fboRenderAlphaID); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, OGLRef.texGColorID, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, OGLRef.texGPolyID, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, OGLRef.texGFogAttrID, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID, 0); + + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) + { + INFO("OpenGL: Failed to create FBOs!\n"); + this->DestroyFBOs(); + + return OGLERROR_FBO_CREATE_ERROR; + } + + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.fboPostprocessID); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, OGLRef.texGColorID, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, OGLRef.texFinalColorID, 0); @@ -2385,6 +2525,7 @@ void OpenGLRenderer_1_2::DestroyFBOs() glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glDeleteFramebuffersEXT(1, &OGLRef.fboClearImageID); glDeleteFramebuffersEXT(1, &OGLRef.fboRenderID); + glDeleteFramebuffersEXT(1, &OGLRef.fboRenderAlphaID); glDeleteFramebuffersEXT(1, &OGLRef.fboPostprocessID); glDeleteTextures(1, &OGLRef.texCIColorID); glDeleteTextures(1, &OGLRef.texCIFogAttrID); @@ -2394,16 +2535,18 @@ void OpenGLRenderer_1_2::DestroyFBOs() glDeleteTextures(1, &OGLRef.texGPolyID); glDeleteTextures(1, &OGLRef.texGFogAttrID); glDeleteTextures(1, &OGLRef.texGDepthStencilID); + glDeleteTextures(1, &OGLRef.texGDepthStencilAlphaID); glDeleteTextures(1, &OGLRef.texZeroAlphaPixelMaskID); OGLRef.fboClearImageID = 0; OGLRef.fboRenderID = 0; + OGLRef.fboRenderAlphaID = 0; OGLRef.fboPostprocessID = 0; this->isFBOSupported = false; } -Render3DError OpenGLRenderer_1_2::CreateMultisampledFBO() +Render3DError OpenGLRenderer_1_2::CreateMultisampledFBO(GLsizei numSamples) { OGLRenderRef &OGLRef = *this->ref; @@ -2413,20 +2556,14 @@ Render3DError OpenGLRenderer_1_2::CreateMultisampledFBO() glGenRenderbuffersEXT(1, &OGLRef.rboMSGFogAttrID); glGenRenderbuffersEXT(1, &OGLRef.rboMSGDepthStencilID); - GLint maxSamples = this->_deviceInfo.maxSamples; - if (maxSamples > OGLRENDER_MAX_MULTISAMPLES) - { - maxSamples = OGLRENDER_MAX_MULTISAMPLES; - } - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGColorID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, numSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGPolyID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, numSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGFogAttrID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, numSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGDepthStencilID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_DEPTH24_STENCIL8_EXT, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, numSamples, GL_DEPTH24_STENCIL8_EXT, this->_framebufferWidth, this->_framebufferHeight); // Set up multisampled rendering FBO glGenFramebuffersEXT(1, &OGLRef.fboMSIntermediateRenderID); @@ -3218,6 +3355,110 @@ Render3DError OpenGLRenderer_1_2::DisableVertexAttributes() return OGLERROR_NOERR; } +Render3DError OpenGLRenderer_1_2::ZeroDstAlphaPass(const POLYLIST *polyList, const INDEXLIST *indexList, bool enableAlphaBlending, size_t indexOffset, bool lastPolyTreatedAsTranslucent) +{ + OGLRenderRef &OGLRef = *this->ref; + + if (!this->isShaderSupported || !this->isFBOSupported || !this->isVBOSupported) + { + return OGLERROR_FEATURE_UNSUPPORTED; + } + + // For now, we're not going to support this pass with MSAA, so skip it when running MSAA. + if (this->isMultisampledFBOSupported && (OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID)) + { + return OGLERROR_NOERR; + } + + // Pre Pass: Fill in the stencil buffer based on the alpha of the current framebuffer color. + // Fully transparent pixels (alpha == 0) -- Set stencil buffer to 0 + // All other pixels (alpha != 0) -- Set stencil buffer to 1 + + this->DisableVertexAttributes(); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, OGLRef.fboRenderAlphaID); + glDrawBuffer(GL_NONE); + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glBlitFramebufferEXT(0, 0, this->_framebufferWidth, this->_framebufferHeight, 0, 0, this->_framebufferWidth, this->_framebufferHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + + glUseProgram(OGLRef.programGeometryZeroDstAlphaID); + glViewport(0, 0, this->_framebufferWidth, this->_framebufferHeight); + glDisable(GL_BLEND); + glEnable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + + glStencilFunc(GL_ALWAYS, 0x80, 0x80); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilMask(0x80); + + glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboPostprocessIndexID); + + if (this->isVAOSupported) + { + glBindVertexArray(OGLRef.vaoPostprocessStatesID); + } + else + { + glEnableVertexAttribArray(OGLVertexAttributeID_Position); + glEnableVertexAttribArray(OGLVertexAttributeID_TexCoord0); + glVertexAttribPointer(OGLVertexAttributeID_Position, 2, GL_FLOAT, GL_FALSE, 0, 0); + glVertexAttribPointer(OGLVertexAttributeID_TexCoord0, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)(sizeof(GLfloat) * 8)); + } + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0); + + if (this->isVAOSupported) + { + glBindVertexArray(0); + } + else + { + glDisableVertexAttribArray(OGLVertexAttributeID_Position); + glDisableVertexAttribArray(OGLVertexAttributeID_TexCoord0); + } + + // Setup for multiple pass alpha poly drawing + glUseProgram(OGLRef.programGeometryID); + glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); + glUniform1i(OGLRef.uniformPolyDrawShadow, GL_FALSE); + + glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID); + this->EnableVertexAttributes(); + + // Draw the alpha polys, touching fully transparent pixels only once. + static const GLenum RenderAlphaDrawList[3] = {GL_COLOR_ATTACHMENT0_EXT, GL_NONE, GL_NONE}; + glDrawBuffers(3, RenderAlphaDrawList); + glEnable(GL_DEPTH_TEST); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + glDepthMask(GL_FALSE); + + glStencilFunc(GL_NOTEQUAL, 0x80, 0x80); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilMask(0x80); + + this->DrawPolygonsForIndexRange(polyList, indexList, polyList->opaqueCount, polyList->count - 1, indexOffset, lastPolyTreatedAsTranslucent); + + // Restore OpenGL states back to normal. + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, OGLRef.selectedRenderingFBO); + glDrawBuffers(3, RenderDrawList); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + if (enableAlphaBlending) + { + glEnable(GL_BLEND); + } + else + { + glDisable(GL_BLEND); + } + + return OGLERROR_NOERR; +} + Render3DError OpenGLRenderer_1_2::DownsampleFBO() { OGLRenderRef &OGLRef = *this->ref; @@ -3508,6 +3749,8 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D &engine) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); + this->_needsZeroDstAlphaPass = true; + return OGLERROR_NOERR; } @@ -3540,8 +3783,13 @@ Render3DError OpenGLRenderer_1_2::RenderGeometry(const GFX3D_State &renderState, this->DrawPolygonsForIndexRange(polyList, indexList, 0, polyList->opaqueCount - 1, indexOffset, lastPolyTreatedAsTranslucent); } - if (polyList->opaqueCount != polyList->count) + if (polyList->opaqueCount < polyList->count) { + if (this->_needsZeroDstAlphaPass) + { + this->ZeroDstAlphaPass(polyList, indexList, renderState.enableAlphaBlending, indexOffset, lastPolyTreatedAsTranslucent); + } + this->DrawPolygonsForIndexRange(polyList, indexList, polyList->opaqueCount, polyList->count - 1, indexOffset, lastPolyTreatedAsTranslucent); } @@ -3849,7 +4097,7 @@ Render3DError OpenGLRenderer_1_2::ClearUsingImage(const u16 *__restrict colorBuf return OGLERROR_NOERR; } -Render3DError OpenGLRenderer_1_2::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const +Render3DError OpenGLRenderer_1_2::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) { OGLRenderRef &OGLRef = *this->ref; @@ -3876,6 +4124,8 @@ Render3DError OpenGLRenderer_1_2::ClearUsingValues(const FragmentColor &clearCol glClear(GL_COLOR_BUFFER_BIT); glDrawBuffers(3, RenderDrawList); + + this->_needsZeroDstAlphaPass = (clearColor6665.a == 0); } else { @@ -4264,6 +4514,13 @@ Render3DError OpenGLRenderer_1_2::SetFramebufferSize(size_t w, size_t h) this->_mappedFramebuffer = NULL; } + if (this->isShaderSupported && this->isFBOSupported && this->isVBOSupported) + { + glActiveTextureARB(GL_TEXTURE0_ARB); + glBindTexture(GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT, w, h, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, NULL); + } + if (this->isShaderSupported || this->isFBOSupported) { glActiveTextureARB(GL_TEXTURE0_ARB + OGLTextureUnitID_FinalColor); @@ -4291,16 +4548,20 @@ Render3DError OpenGLRenderer_1_2::SetFramebufferSize(size_t w, size_t h) if (this->isMultisampledFBOSupported) { - GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; + GLsizei maxSamplesOGL = (GLsizei)this->_deviceInfo.maxSamples; + if (maxSamplesOGL > OGLRENDER_MAX_MULTISAMPLES) + { + maxSamplesOGL = OGLRENDER_MAX_MULTISAMPLES; + } glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGColorID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamplesOGL, GL_RGBA, w, h); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGPolyID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamplesOGL, GL_RGBA, w, h); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGFogAttrID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamplesOGL, GL_RGBA, w, h); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGDepthStencilID); - glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_DEPTH24_STENCIL8_EXT, w, h); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamplesOGL, GL_DEPTH24_STENCIL8_EXT, w, h); } glActiveTextureARB(GL_TEXTURE0_ARB); @@ -4456,6 +4717,8 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D &engine) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); + this->_needsZeroDstAlphaPass = true; + return OGLERROR_NOERR; } diff --git a/desmume/src/OGLRender.h b/desmume/src/OGLRender.h index 010753e4a..ea25f0028 100755 --- a/desmume/src/OGLRender.h +++ b/desmume/src/OGLRender.h @@ -434,6 +434,7 @@ struct OGLRenderRef GLuint texGPolyID; GLuint texZeroAlphaPixelMaskID; GLuint texGDepthStencilID; + GLuint texGDepthStencilAlphaID; GLuint texFinalColorID; GLuint rboMSGColorID; @@ -443,6 +444,7 @@ struct OGLRenderRef GLuint fboClearImageID; GLuint fboRenderID; + GLuint fboRenderAlphaID; GLuint fboPostprocessID; GLuint fboMSIntermediateRenderID; GLuint selectedRenderingFBO; @@ -452,6 +454,10 @@ struct OGLRenderRef GLuint fragmentGeometryShaderID; GLuint programGeometryID; + GLuint vtxShaderGeometryZeroDstAlphaID; + GLuint fragShaderGeometryZeroDstAlphaID; + GLuint programGeometryZeroDstAlphaID; + GLuint vertexZeroAlphaPixelMaskShaderID; GLuint vertexEdgeMarkShaderID; GLuint vertexFogShaderID; @@ -626,6 +632,7 @@ protected: FragmentColor *_mappedFramebuffer; FragmentColor *_workingTextureUnpackBuffer; bool _pixelReadNeedsFinish; + bool _needsZeroDstAlphaPass; size_t _currentPolyIndex; OGLTextureUnitID _lastTextureDrawTarget; @@ -641,23 +648,23 @@ protected: virtual void DestroyPBOs() = 0; virtual Render3DError CreateFBOs() = 0; virtual void DestroyFBOs() = 0; - virtual Render3DError CreateMultisampledFBO() = 0; + virtual Render3DError CreateMultisampledFBO(GLsizei numSamples) = 0; virtual void DestroyMultisampledFBO() = 0; - virtual Render3DError InitGeometryProgram(const char *geometryVtxShaderCString, const char *geometryFragShaderCString) = 0; + virtual Render3DError InitGeometryProgram(const char *geometryVtxShaderCString, const char *geometryFragShaderCString, const char *geometryAlphaVtxShaderCString, const char *geometryAlphaFragShaderCString) = 0; virtual void DestroyGeometryProgram() = 0; virtual Render3DError CreateVAOs() = 0; virtual void DestroyVAOs() = 0; virtual Render3DError InitFinalRenderStates(const std::set *oglExtensionSet) = 0; virtual Render3DError InitTables() = 0; - virtual Render3DError InitPostprocessingPrograms(const char *zeroAlphaPixelMaskVtxShaderCString, - const char *zeroAlphaPixelMaskFragShaderCString, - const char *edgeMarkVtxShaderCString, - const char *edgeMarkFragShaderCString, - const char *fogVtxShaderCString, - const char *fogFragShaderCString, - const char *framebufferOutputVtxShaderCString, - const char *framebufferOutputRGBA6665FragShaderCString, - const char *framebufferOutputRGBA8888FragShaderCString) = 0; + virtual Render3DError InitPostprocessingPrograms(const char *zeroAlphaPixelMaskVtxShader, + const char *zeroAlphaPixelMaskFragShader, + const char *edgeMarkVtxShader, + const char *edgeMarkFragShader, + const char *fogVtxShader, + const char *fogFragShader, + const char *framebufferOutputVtxShader, + const char *framebufferOutputRGBA6665FragShader, + const char *framebufferOutputRGBA8888FragShader) = 0; virtual Render3DError DestroyPostprocessingPrograms() = 0; virtual Render3DError InitZeroAlphaPixelMaskProgramBindings() = 0; virtual Render3DError InitZeroAlphaPixelMaskProgramShaderLocations() = 0; @@ -668,9 +675,10 @@ protected: virtual Render3DError InitFramebufferOutputProgramBindings() = 0; virtual Render3DError InitFramebufferOutputShaderLocations() = 0; - virtual Render3DError LoadGeometryShaders(std::string &outVertexShaderProgram, std::string &outFragmentShaderProgram) = 0; virtual Render3DError InitGeometryProgramBindings() = 0; virtual Render3DError InitGeometryProgramShaderLocations() = 0; + virtual Render3DError InitGeometryZeroDstAlphaProgramBindings() = 0; + virtual Render3DError InitGeometryZeroDstAlphaProgramShaderLocations() = 0; virtual Render3DError CreateToonTable() = 0; virtual Render3DError DestroyToonTable() = 0; virtual Render3DError UploadClearImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const u8 *__restrict fogBuffer, const u8 *__restrict polyIDBuffer) = 0; @@ -710,27 +718,28 @@ protected: virtual void DestroyPBOs(); virtual Render3DError CreateFBOs(); virtual void DestroyFBOs(); - virtual Render3DError CreateMultisampledFBO(); + virtual Render3DError CreateMultisampledFBO(GLsizei numSamples); virtual void DestroyMultisampledFBO(); virtual Render3DError CreateVAOs(); virtual void DestroyVAOs(); virtual Render3DError InitFinalRenderStates(const std::set *oglExtensionSet); virtual Render3DError InitTables(); - virtual Render3DError InitGeometryProgram(const char *geometryVtxShaderCString, const char *geometryFragShaderCString); - virtual Render3DError LoadGeometryShaders(std::string &outVertexShaderProgram, std::string &outFragmentShaderProgram); + virtual Render3DError InitGeometryProgram(const char *geometryVtxShaderCString, const char *geometryFragShaderCString, const char *geometryAlphaVtxShaderCString, const char *geometryAlphaFragShaderCString); virtual Render3DError InitGeometryProgramBindings(); virtual Render3DError InitGeometryProgramShaderLocations(); + virtual Render3DError InitGeometryZeroDstAlphaProgramBindings(); + virtual Render3DError InitGeometryZeroDstAlphaProgramShaderLocations(); virtual void DestroyGeometryProgram(); - virtual Render3DError InitPostprocessingPrograms(const char *zeroAlphaPixelMaskVtxShaderCString, - const char *zeroAlphaPixelMaskFragShaderCString, - const char *edgeMarkVtxShaderCString, - const char *edgeMarkFragShaderCString, - const char *fogVtxShaderCString, - const char *fogFragShaderCString, - const char *framebufferOutputVtxShaderCString, - const char *framebufferOutputRGBA6665FragShaderCString, - const char *framebufferOutputRGBA8888FragShaderCString); + virtual Render3DError InitPostprocessingPrograms(const char *zeroAlphaPixelMaskVtxShader, + const char *zeroAlphaPixelMaskFragShader, + const char *edgeMarkVtxShader, + const char *edgeMarkFragShader, + const char *fogVtxShader, + const char *fogFragShader, + const char *framebufferOutputVtxShader, + const char *framebufferOutputRGBA6665FragShader, + const char *framebufferOutputRGBA8888FragShader); virtual Render3DError DestroyPostprocessingPrograms(); virtual Render3DError InitZeroAlphaPixelMaskProgramBindings(); virtual Render3DError InitZeroAlphaPixelMaskProgramShaderLocations(); @@ -748,6 +757,7 @@ protected: virtual void GetExtensionSet(std::set *oglExtensionSet); virtual Render3DError EnableVertexAttributes(); virtual Render3DError DisableVertexAttributes(); + virtual Render3DError ZeroDstAlphaPass(const POLYLIST *polyList, const INDEXLIST *indexList, bool enableAlphaBlending, size_t indexOffset, bool lastPolyTreatedAsTranslucent); virtual Render3DError DownsampleFBO(); virtual Render3DError ReadBackPixels(); @@ -759,7 +769,7 @@ protected: virtual Render3DError EndRender(const u64 frameCount); virtual Render3DError ClearUsingImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const u8 *__restrict fogBuffer, const u8 *__restrict polyIDBuffer); - virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; + virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes); virtual void SetPolygonIndex(const size_t index); virtual Render3DError SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer); diff --git a/desmume/src/OGLRender_3_2.cpp b/desmume/src/OGLRender_3_2.cpp index 47badb198..8ce19bfae 100644 --- a/desmume/src/OGLRender_3_2.cpp +++ b/desmume/src/OGLRender_3_2.cpp @@ -271,6 +271,39 @@ static const char *GeometryFragShader_150 = {"\ }\n\ "}; +// Vertex shader for determining which pixels have a zero alpha, GLSL 1.50 +static const char *GeometryZeroDstAlphaPixelMaskVtxShader_150 = {"\ + #version 150\n\ + \n\ + in vec2 inPosition;\n\ + in vec2 inTexCoord0;\n\ + out vec2 texCoord;\n\ + \n\ + void main()\n\ + {\n\ + texCoord = inTexCoord0;\n\ + gl_Position = vec4(inPosition, 0.0, 1.0);\n\ + }\n\ +"}; + +// Fragment shader for determining which pixels have a zero alpha, GLSL 1.50 +static const char *GeometryZeroDstAlphaPixelMaskFragShader_150 = {"\ + #version 150\n\ + \n\ + in vec2 texCoord;\n\ + uniform sampler2D texInFragColor;\n\ + \n\ + void main()\n\ + {\n\ + vec4 inFragColor = texture(texInFragColor, texCoord);\n\ + \n\ + if (inFragColor.a <= 0.001)\n\ + {\n\ + discard;\n\ + }\n\ + }\n\ +"}; + // Vertex shader for determining which pixels have a zero alpha, GLSL 1.50 static const char *ZeroAlphaPixelMaskVtxShader_150 = {"\ #version 150\n\ @@ -587,12 +620,8 @@ Render3DError OpenGLRenderer_3_2::InitExtensions() glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyOGL); this->_deviceInfo.maxAnisotropy = (float)maxAnisotropyOGL; - GLint maxSamplesOGL = 0; - glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesOGL); - this->_deviceInfo.maxSamples = (u8)maxSamplesOGL; - - _deviceInfo.isEdgeMarkSupported = true; - _deviceInfo.isFogSupported = true; + this->_deviceInfo.isEdgeMarkSupported = true; + this->_deviceInfo.isFogSupported = true; // Initialize OpenGL this->InitTables(); @@ -614,40 +643,23 @@ Render3DError OpenGLRenderer_3_2::InitExtensions() this->willFlipOnlyFramebufferOnGPU = true; this->willFlipAndConvertFramebufferOnGPU = true; - std::string geometryVtxShaderString; - std::string geometryFragShaderString; - error = this->LoadGeometryShaders(geometryVtxShaderString, geometryFragShaderString); + error = this->InitGeometryProgram(GeometryVtxShader_150, GeometryFragShader_150, + GeometryZeroDstAlphaPixelMaskVtxShader_150, GeometryZeroDstAlphaPixelMaskFragShader_150); if (error != OGLERROR_NOERR) { this->isShaderSupported = false; return error; } - error = this->InitGeometryProgram(geometryVtxShaderString.c_str(), geometryFragShaderString.c_str()); - if (error != OGLERROR_NOERR) - { - this->isShaderSupported = false; - return error; - } - - std::string zeroAlphaPixelMaskVtxShaderString = std::string(ZeroAlphaPixelMaskVtxShader_150); - std::string zeroAlphaPixelMaskFragShaderString = std::string(ZeroAlphaPixelMaskFragShader_150); - std::string edgeMarkVtxShaderString = std::string(EdgeMarkVtxShader_150); - std::string edgeMarkFragShaderString = std::string(EdgeMarkFragShader_150); - std::string fogVtxShaderString = std::string(FogVtxShader_150); - std::string fogFragShaderString = std::string(FogFragShader_150); - std::string framebufferOutputVtxShaderString = std::string(FramebufferOutputVtxShader_150); - std::string framebufferOutputFragShaderString = std::string(FramebufferOutputFragShader_150); - - error = this->InitPostprocessingPrograms(zeroAlphaPixelMaskVtxShaderString.c_str(), - zeroAlphaPixelMaskFragShaderString.c_str(), - edgeMarkVtxShaderString.c_str(), - edgeMarkFragShaderString.c_str(), - fogVtxShaderString.c_str(), - fogFragShaderString.c_str(), - framebufferOutputVtxShaderString.c_str(), - framebufferOutputFragShaderString.c_str(), - framebufferOutputFragShaderString.c_str()); + error = this->InitPostprocessingPrograms(ZeroAlphaPixelMaskVtxShader_150, + ZeroAlphaPixelMaskFragShader_150, + EdgeMarkVtxShader_150, + EdgeMarkFragShader_150, + FogVtxShader_150, + FogFragShader_150, + FramebufferOutputVtxShader_150, + FramebufferOutputFragShader_150, + FramebufferOutputFragShader_150); if (error != OGLERROR_NOERR) { this->DestroyGeometryProgram(); @@ -674,17 +686,28 @@ Render3DError OpenGLRenderer_3_2::InitExtensions() } this->isMultisampledFBOSupported = true; - error = this->CreateMultisampledFBO(); - if (error != OGLERROR_NOERR) + + GLint maxSamplesOGL = 0; + glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesOGL); + this->_deviceInfo.maxSamples = (u8)maxSamplesOGL; + + if (maxSamplesOGL >= 2) + { + if (maxSamplesOGL > OGLRENDER_MAX_MULTISAMPLES) + { + maxSamplesOGL = OGLRENDER_MAX_MULTISAMPLES; + } + + error = this->CreateMultisampledFBO(maxSamplesOGL); + if (error != OGLERROR_NOERR) + { + this->isMultisampledFBOSupported = false; + } + } + else { this->isMultisampledFBOSupported = false; - - if (error == OGLERROR_FBO_CREATE_ERROR) - { - // Return on OGLERROR_FBO_CREATE_ERROR, since a v3.0 driver should be able to - // support FBOs. - return error; - } + INFO("OpenGL: Driver does not support at least 2x multisampled FBOs.\n"); } this->InitFinalRenderStates(&oglExtensionSet); // This must be done last @@ -817,6 +840,7 @@ Render3DError OpenGLRenderer_3_2::CreateFBOs() glGenTextures(1, &OGLRef.texGFogAttrID); glGenTextures(1, &OGLRef.texGPolyID); glGenTextures(1, &OGLRef.texGDepthStencilID); + glGenTextures(1, &OGLRef.texGDepthStencilAlphaID); glGenTextures(1, &OGLRef.texZeroAlphaPixelMaskID); glActiveTexture(GL_TEXTURE0 + OGLTextureUnitID_DepthStencil); @@ -862,6 +886,14 @@ Render3DError OpenGLRenderer_3_2::CreateFBOs() glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, this->_framebufferWidth, this->_framebufferHeight, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + glBindTexture(GL_TEXTURE_2D, OGLRef.texCIColorID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -896,6 +928,7 @@ Render3DError OpenGLRenderer_3_2::CreateFBOs() // Set up FBOs glGenFramebuffers(1, &OGLRef.fboClearImageID); glGenFramebuffers(1, &OGLRef.fboRenderID); + glGenFramebuffers(1, &OGLRef.fboRenderAlphaID); glGenFramebuffers(1, &OGLRef.fboPostprocessID); glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.fboClearImageID); @@ -932,6 +965,23 @@ Render3DError OpenGLRenderer_3_2::CreateFBOs() glDrawBuffers(3, RenderDrawList); glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.fboRenderAlphaID); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, OGLRef.texGColorID, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, OGLRef.texGPolyID, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, OGLRef.texGFogAttrID, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + INFO("OpenGL: Failed to create FBOs!\n"); + this->DestroyFBOs(); + + return OGLERROR_FBO_CREATE_ERROR; + } + + glDrawBuffer(GL_COLOR_ATTACHMENT0); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.fboPostprocessID); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, OGLRef.texGColorID, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, OGLRef.texFinalColorID, 0); @@ -967,6 +1017,7 @@ void OpenGLRenderer_3_2::DestroyFBOs() glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &OGLRef.fboClearImageID); glDeleteFramebuffers(1, &OGLRef.fboRenderID); + glDeleteFramebuffers(1, &OGLRef.fboRenderAlphaID); glDeleteFramebuffers(1, &OGLRef.fboPostprocessID); glDeleteTextures(1, &OGLRef.texCIColorID); glDeleteTextures(1, &OGLRef.texCIFogAttrID); @@ -976,32 +1027,19 @@ void OpenGLRenderer_3_2::DestroyFBOs() glDeleteTextures(1, &OGLRef.texGPolyID); glDeleteTextures(1, &OGLRef.texGFogAttrID); glDeleteTextures(1, &OGLRef.texGDepthStencilID); + glDeleteTextures(1, &OGLRef.texGDepthStencilAlphaID); glDeleteTextures(1, &OGLRef.texZeroAlphaPixelMaskID); OGLRef.fboClearImageID = 0; OGLRef.fboRenderID = 0; + OGLRef.fboRenderAlphaID = 0; OGLRef.fboPostprocessID = 0; this->isFBOSupported = false; } -Render3DError OpenGLRenderer_3_2::CreateMultisampledFBO() +Render3DError OpenGLRenderer_3_2::CreateMultisampledFBO(GLsizei numSamples) { - // Check the maximum number of samples that the GPU supports and use that. - // Since our target resolution is only 256x192 pixels, using the most samples - // possible is the best thing to do. - GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; - - if (maxSamples < 2) - { - INFO("OpenGL: GPU does not support at least 2x multisampled FBOs. Multisample antialiasing will be disabled.\n"); - return OGLERROR_FEATURE_UNSUPPORTED; - } - else if (maxSamples > OGLRENDER_MAX_MULTISAMPLES) - { - maxSamples = OGLRENDER_MAX_MULTISAMPLES; - } - OGLRenderRef &OGLRef = *this->ref; // Set up FBO render targets @@ -1011,13 +1049,13 @@ Render3DError OpenGLRenderer_3_2::CreateMultisampledFBO() glGenRenderbuffers(1, &OGLRef.rboMSGDepthStencilID); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGColorID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGPolyID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGFogAttrID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL_RGBA, this->_framebufferWidth, this->_framebufferHeight); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGDepthStencilID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_DEPTH24_STENCIL8, this->_framebufferWidth, this->_framebufferHeight); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL_DEPTH24_STENCIL8, this->_framebufferWidth, this->_framebufferHeight); // Set up multisampled rendering FBO glGenFramebuffers(1, &OGLRef.fboMSIntermediateRenderID); @@ -1113,17 +1151,6 @@ void OpenGLRenderer_3_2::DestroyVAOs() this->isVAOSupported = false; } -Render3DError OpenGLRenderer_3_2::LoadGeometryShaders(std::string &outVertexShaderProgram, std::string &outFragmentShaderProgram) -{ - outVertexShaderProgram.clear(); - outFragmentShaderProgram.clear(); - - outVertexShaderProgram = std::string(GeometryVtxShader_150); - outFragmentShaderProgram = std::string(GeometryFragShader_150); - - return OGLERROR_NOERR; -} - Render3DError OpenGLRenderer_3_2::InitGeometryProgramBindings() { OGLRenderRef &OGLRef = *this->ref; @@ -1181,6 +1208,27 @@ Render3DError OpenGLRenderer_3_2::InitGeometryProgramShaderLocations() return OGLERROR_NOERR; } +Render3DError OpenGLRenderer_3_2::InitGeometryZeroDstAlphaProgramBindings() +{ + OGLRenderRef &OGLRef = *this->ref; + + glBindAttribLocation(OGLRef.programGeometryZeroDstAlphaID, OGLVertexAttributeID_Position, "inPosition"); + glBindAttribLocation(OGLRef.programGeometryZeroDstAlphaID, OGLVertexAttributeID_TexCoord0, "inTexCoord0"); + + return OGLERROR_NOERR; +} + +Render3DError OpenGLRenderer_3_2::InitGeometryZeroDstAlphaProgramShaderLocations() +{ + OGLRenderRef &OGLRef = *this->ref; + + glUseProgram(OGLRef.programGeometryZeroDstAlphaID); + const GLint uniformTexGColor = glGetUniformLocation(OGLRef.programGeometryZeroDstAlphaID, "texInFragColor"); + glUniform1i(uniformTexGColor, OGLTextureUnitID_GColor); + + return OGLERROR_NOERR; +} + void OpenGLRenderer_3_2::DestroyGeometryProgram() { if (!this->isShaderSupported) @@ -1203,6 +1251,12 @@ void OpenGLRenderer_3_2::DestroyGeometryProgram() glDeleteShader(OGLRef.vertexGeometryShaderID); glDeleteShader(OGLRef.fragmentGeometryShaderID); + glDetachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDetachShader(OGLRef.programGeometryZeroDstAlphaID, OGLRef.fragShaderGeometryZeroDstAlphaID); + glDeleteProgram(OGLRef.programGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.vtxShaderGeometryZeroDstAlphaID); + glDeleteShader(OGLRef.fragShaderGeometryZeroDstAlphaID); + this->DestroyToonTable(); this->isShaderSupported = false; @@ -1232,11 +1286,88 @@ Render3DError OpenGLRenderer_3_2::DisableVertexAttributes() return OGLERROR_NOERR; } +Render3DError OpenGLRenderer_3_2::ZeroDstAlphaPass(const POLYLIST *polyList, const INDEXLIST *indexList, bool enableAlphaBlending, size_t indexOffset, bool lastPolyTreatedAsTranslucent) +{ + OGLRenderRef &OGLRef = *this->ref; + + // For now, we're not going to support this pass with MSAA, so skip it when running MSAA. + if (this->isMultisampledFBOSupported && (OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID)) + { + return OGLERROR_NOERR; + } + + // Pre Pass: Fill in the stencil buffer based on the alpha of the current framebuffer color. + // Fully transparent pixels (alpha == 0) -- Set stencil buffer to 0 + // All other pixels (alpha != 0) -- Set stencil buffer to 1 + + this->DisableVertexAttributes(); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OGLRef.fboRenderAlphaID); + glDrawBuffer(GL_NONE); + glClearBufferfi(GL_DEPTH_STENCIL, 0, 0.0f, 0); + glBlitFramebuffer(0, 0, this->_framebufferWidth, this->_framebufferHeight, 0, 0, this->_framebufferWidth, this->_framebufferHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + + glUseProgram(OGLRef.programGeometryZeroDstAlphaID); + glViewport(0, 0, this->_framebufferWidth, this->_framebufferHeight); + glDisable(GL_BLEND); + glEnable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + + glStencilFunc(GL_ALWAYS, 0x80, 0x80); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilMask(0x80); + + glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboPostprocessIndexID); + glBindVertexArray(OGLRef.vaoPostprocessStatesID); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0); + glBindVertexArray(0); + + // Setup for multiple pass alpha poly drawing + glUseProgram(OGLRef.programGeometryID); + glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); + glUniform1i(OGLRef.uniformPolyDrawShadow, GL_FALSE); + + glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboGeometryVtxID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, OGLRef.iboGeometryIndexID); + this->EnableVertexAttributes(); + + // Draw the alpha polys, touching fully transparent pixels only once. + static const GLenum RenderAlphaDrawList[3] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE}; + glDrawBuffers(3, RenderAlphaDrawList); + glEnable(GL_DEPTH_TEST); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + glDepthMask(GL_FALSE); + + glStencilFunc(GL_NOTEQUAL, 0x80, 0x80); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilMask(0x80); + + this->DrawPolygonsForIndexRange(polyList, indexList, polyList->opaqueCount, polyList->count - 1, indexOffset, lastPolyTreatedAsTranslucent); + + // Restore OpenGL states back to normal. + glBindFramebuffer(GL_FRAMEBUFFER, OGLRef.selectedRenderingFBO); + glDrawBuffers(3, RenderDrawList); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + if (enableAlphaBlending) + { + glEnable(GL_BLEND); + } + else + { + glDisable(GL_BLEND); + } + + return OGLERROR_NOERR; +} + Render3DError OpenGLRenderer_3_2::DownsampleFBO() { OGLRenderRef &OGLRef = *this->ref; - if (OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID) + if (this->isMultisampledFBOSupported && (OGLRef.selectedRenderingFBO == OGLRef.fboMSIntermediateRenderID)) { glBindFramebuffer(GL_READ_FRAMEBUFFER, OGLRef.fboMSIntermediateRenderID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OGLRef.fboRenderID); @@ -1470,6 +1601,8 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D &engine) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); + this->_needsZeroDstAlphaPass = true; + return OGLERROR_NOERR; } @@ -1616,7 +1749,7 @@ Render3DError OpenGLRenderer_3_2::ClearUsingImage(const u16 *__restrict colorBuf return OGLERROR_NOERR; } -Render3DError OpenGLRenderer_3_2::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const +Render3DError OpenGLRenderer_3_2::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) { OGLRenderRef &OGLRef = *this->ref; OGLRef.selectedRenderingFBO = (CommonSettings.GFX3D_Renderer_Multisample && this->isMultisampledFBOSupported) ? OGLRef.fboMSIntermediateRenderID : OGLRef.fboRenderID; @@ -1633,6 +1766,8 @@ Render3DError OpenGLRenderer_3_2::ClearUsingValues(const FragmentColor &clearCol glClearBufferfv(GL_COLOR, 1, oglPolyID); // texGPolyID glClearBufferfv(GL_COLOR, 2, oglFogAttr); // texGFogAttrID + this->_needsZeroDstAlphaPass = (clearColor6665.a == 0); + return OGLERROR_NOERR; } @@ -1786,20 +1921,27 @@ Render3DError OpenGLRenderer_3_2::SetFramebufferSize(size_t w, size_t h) if (this->isMultisampledFBOSupported) { - GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; + GLsizei maxSamplesOGL = (GLsizei)this->_deviceInfo.maxSamples; + if (maxSamplesOGL > OGLRENDER_MAX_MULTISAMPLES) + { + maxSamplesOGL = OGLRENDER_MAX_MULTISAMPLES; + } glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGColorID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, w, h); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamplesOGL, GL_RGBA, w, h); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGPolyID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, w, h); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamplesOGL, GL_RGBA, w, h); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGFogAttrID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, w, h); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamplesOGL, GL_RGBA, w, h); glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGDepthStencilID); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_DEPTH24_STENCIL8, w, h); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamplesOGL, GL_DEPTH24_STENCIL8, w, h); } glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, OGLRef.texGDepthStencilAlphaID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + const size_t newFramebufferColorSizeBytes = w * h * sizeof(FragmentColor); glBufferData(GL_PIXEL_PACK_BUFFER, newFramebufferColorSizeBytes, NULL, GL_STREAM_READ); diff --git a/desmume/src/OGLRender_3_2.h b/desmume/src/OGLRender_3_2.h index 07a214e38..1b4026223 100644 --- a/desmume/src/OGLRender_3_2.h +++ b/desmume/src/OGLRender_3_2.h @@ -73,19 +73,21 @@ protected: virtual Render3DError InitFramebufferOutputShaderLocations(); virtual Render3DError CreateFBOs(); virtual void DestroyFBOs(); - virtual Render3DError CreateMultisampledFBO(); + virtual Render3DError CreateMultisampledFBO(GLsizei numSamples); virtual void DestroyMultisampledFBO(); virtual Render3DError CreateVAOs(); virtual void DestroyVAOs(); - virtual Render3DError LoadGeometryShaders(std::string &outVertexShaderProgram, std::string &outFragmentShaderProgram); virtual Render3DError InitGeometryProgramBindings(); virtual Render3DError InitGeometryProgramShaderLocations(); + virtual Render3DError InitGeometryZeroDstAlphaProgramBindings(); + virtual Render3DError InitGeometryZeroDstAlphaProgramShaderLocations(); virtual void DestroyGeometryProgram(); virtual void GetExtensionSet(std::set *oglExtensionSet); virtual Render3DError EnableVertexAttributes(); virtual Render3DError DisableVertexAttributes(); + virtual Render3DError ZeroDstAlphaPass(const POLYLIST *polyList, const INDEXLIST *indexList, bool enableAlphaBlending, size_t indexOffset, bool lastPolyTreatedAsTranslucent); virtual Render3DError DownsampleFBO(); virtual Render3DError ReadBackPixels(); virtual Render3DError BeginRender(const GFX3D &engine); @@ -96,7 +98,7 @@ protected: virtual Render3DError DestroyToonTable(); virtual Render3DError UpdateToonTable(const u16 *toonTableBuffer); virtual Render3DError ClearUsingImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const u8 *__restrict fogBuffer, const u8 *__restrict polyIDBuffer); - virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; + virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes); virtual void SetPolygonIndex(const size_t index); virtual Render3DError SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer); diff --git a/desmume/src/rasterize.cpp b/desmume/src/rasterize.cpp index 29540d2a7..75b0be436 100644 --- a/desmume/src/rasterize.cpp +++ b/desmume/src/rasterize.cpp @@ -2059,7 +2059,7 @@ Render3DError SoftRasterizerRenderer::ClearUsingImage(const u16 *__restrict colo return RENDER3DERROR_NOERR; } -Render3DError SoftRasterizerRenderer::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const +Render3DError SoftRasterizerRenderer::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) { for (size_t i = 0; i < (this->_framebufferWidth * this->_framebufferHeight); i++) { @@ -2217,7 +2217,7 @@ Render3DError SoftRasterizerRenderer::SetFramebufferSize(size_t w, size_t h) #ifdef ENABLE_SSE2 -Render3DError SoftRasterizerRenderer_SSE2::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const +Render3DError SoftRasterizerRenderer_SSE2::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) { const __m128i color_vec128 = _mm_set1_epi32(clearColor6665.color); const __m128i attrDepth_vec128 = _mm_set1_epi32(clearAttributes.depth); diff --git a/desmume/src/rasterize.h b/desmume/src/rasterize.h index 51d60f6dc..9b608bd7e 100644 --- a/desmume/src/rasterize.h +++ b/desmume/src/rasterize.h @@ -111,7 +111,7 @@ protected: virtual Render3DError EndRender(const u64 frameCount); virtual Render3DError ClearUsingImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const u8 *__restrict fogBuffer, const u8 *__restrict polyIDBuffer); - virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; + virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes); public: int _debug_drawClippedUserPoly; @@ -150,7 +150,7 @@ public: class SoftRasterizerRenderer_SSE2 : public SoftRasterizerRenderer { - virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; + virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes); }; #endif diff --git a/desmume/src/render3D.cpp b/desmume/src/render3D.cpp index eabc688d7..12154c21d 100644 --- a/desmume/src/render3D.cpp +++ b/desmume/src/render3D.cpp @@ -615,7 +615,7 @@ Render3DError Render3D::ClearUsingImage(const u16 *__restrict colorBuffer, const return RENDER3DERROR_NOERR; } -Render3DError Render3D::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const +Render3DError Render3D::ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) { return RENDER3DERROR_NOERR; } diff --git a/desmume/src/render3D.h b/desmume/src/render3D.h index 55412d6e5..4f413042c 100644 --- a/desmume/src/render3D.h +++ b/desmume/src/render3D.h @@ -177,7 +177,7 @@ protected: virtual Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16); virtual Render3DError ClearUsingImage(const u16 *__restrict colorBuffer, const u32 *__restrict depthBuffer, const u8 *__restrict fogBuffer, const u8 *__restrict polyIDBuffer); - virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; + virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes); virtual Render3DError SetupTexture(const POLY &thePoly, size_t polyRenderIndex); virtual Render3DError SetupViewport(const u32 viewportValue);