diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index 9e2df4624..5f2966d15 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -483,8 +483,9 @@ extern struct TCommonSettings { , GFX3D_LineHack(true) , GFX3D_Zelda_Shadow_Depth_Hack(0) , GFX3D_Renderer_Multisample(false) - , GFX3D_Renderer_TextureDeposterize(false) , GFX3D_Renderer_TextureScalingFactor(1) // Possible values: 1, 2, 4 + , GFX3D_Renderer_TextureDeposterize(false) + , GFX3D_Renderer_TextureSmoothing(false) , GFX3D_TXTHack(false) , GFX3D_PrescaleHD(1) , jit_max_block_size(100) @@ -545,8 +546,9 @@ extern struct TCommonSettings { bool GFX3D_LineHack; int GFX3D_Zelda_Shadow_Depth_Hack; bool GFX3D_Renderer_Multisample; - bool GFX3D_Renderer_TextureDeposterize; int GFX3D_Renderer_TextureScalingFactor; + bool GFX3D_Renderer_TextureDeposterize; + bool GFX3D_Renderer_TextureSmoothing; bool GFX3D_TXTHack; //may not want this on OSX port diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp index f4f71dc39..75014cd43 100644 --- a/desmume/src/OGLRender.cpp +++ b/desmume/src/OGLRender.cpp @@ -293,10 +293,11 @@ static const char *fragmentShader_100 = {"\ uniform int polyMode; \n\ uniform bool polyEnableDepthWrite;\n\ uniform bool polySetNewDepthForTranslucent;\n\ - uniform int polyID; \n\ + uniform int polyID;\n\ \n\ - uniform bool polyEnableTexture; \n\ + uniform bool polyEnableTexture;\n\ uniform bool polyEnableFog;\n\ + uniform bool texSingleBitAlpha;\n\ \n\ vec3 packVec3FromFloat(const float value)\n\ {\n\ @@ -308,6 +309,20 @@ static const char *fragmentShader_100 = {"\ void main() \n\ { \n\ vec4 mainTexColor = (polyEnableTexture) ? texture2D(texRenderObject, vtxTexCoord) : vec4(1.0, 1.0, 1.0, 1.0); \n\ + \n\ + if (texSingleBitAlpha)\n\ + {\n\ + if (mainTexColor.a < 0.500)\n\ + {\n\ + mainTexColor.a = 0.0;\n\ + }\n\ + else\n\ + {\n\ + mainTexColor.rgb = mainTexColor.rgb / mainTexColor.a;\n\ + mainTexColor.a = 1.0;\n\ + }\n\ + }\n\ + \n\ vec4 newFragColor = mainTexColor * vtxColor; \n\ \n\ if(polyMode == 1) \n\ @@ -328,7 +343,7 @@ static const char *fragmentShader_100 = {"\ } \n\ } \n\ \n\ - if (newFragColor.a == 0.0 || (stateEnableAlphaTest && newFragColor.a < stateAlphaTestRef)) \n\ + if (newFragColor.a < 0.001 || (stateEnableAlphaTest && newFragColor.a < stateAlphaTestRef)) \n\ { \n\ discard; \n\ } \n\ @@ -902,8 +917,15 @@ GPU3DInterface gpu3Dgl_3_2 = { OpenGLRenderer::OpenGLRenderer() { - _renderID = RENDERID_OPENGL_AUTO; - _renderName = "OpenGL"; + _deviceInfo.renderID = RENDERID_OPENGL_AUTO; + _deviceInfo.renderName = "OpenGL"; + _deviceInfo.isTexturingSupported = true; + _deviceInfo.isEdgeMarkSupported = true; + _deviceInfo.isFogSupported = true; + _deviceInfo.isTextureSmoothingSupported = true; + _deviceInfo.maxAnisotropy = 1.0f; + _deviceInfo.maxSamples = 0; + _internalRenderingFormat = NDSColorFormat_BGR888_Rev; versionMajor = 0; @@ -1361,6 +1383,15 @@ Render3DError OpenGLRenderer_1_2::InitExtensions() std::set oglExtensionSet; this->GetExtensionSet(&oglExtensionSet); + // Get host GPU device properties + GLfloat maxAnisotropyOGL = 1.0f; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyOGL); + this->_deviceInfo.maxAnisotropy = maxAnisotropyOGL; + + GLint maxSamplesOGL = 0; + glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamplesOGL); + this->_deviceInfo.maxSamples = (u8)maxSamplesOGL; + // Initialize OpenGL this->InitTables(); @@ -1395,21 +1426,32 @@ Render3DError OpenGLRenderer_1_2::InitExtensions() framebufferOutputRGBA8888FragShaderString); if (error != OGLERROR_NOERR) { + this->_deviceInfo.isEdgeMarkSupported = false; + this->_deviceInfo.isFogSupported = false; INFO("OpenGL: Edge mark and fog require OpenGL v2.0 or later. These features will be disabled.\n"); } } else { + this->_deviceInfo.isEdgeMarkSupported = false; + this->_deviceInfo.isFogSupported = false; + this->_deviceInfo.isTextureSmoothingSupported = false; this->isShaderSupported = false; } } else { + this->_deviceInfo.isEdgeMarkSupported = false; + this->_deviceInfo.isFogSupported = false; + this->_deviceInfo.isTextureSmoothingSupported = false; this->isShaderSupported = false; } } else { + this->_deviceInfo.isEdgeMarkSupported = false; + this->_deviceInfo.isFogSupported = false; + this->_deviceInfo.isTextureSmoothingSupported = false; INFO("OpenGL: Shaders are unsupported. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n"); } @@ -1600,6 +1642,7 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgramShaderLocations() OGLRef.uniformPolyEnableTexture = glGetUniformLocation(OGLRef.programGeometryID, "polyEnableTexture"); OGLRef.uniformPolyEnableFog = glGetUniformLocation(OGLRef.programGeometryID, "polyEnableFog"); + OGLRef.uniformTexSingleBitAlpha = glGetUniformLocation(OGLRef.programGeometryID, "texSingleBitAlpha"); return OGLERROR_NOERR; } @@ -2058,8 +2101,7 @@ Render3DError OpenGLRenderer_1_2::CreateMultisampledFBO() // Check the maximum number of samples that the driver 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 = 0; - glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); + GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; if (maxSamples < 2) { @@ -3071,6 +3113,7 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT if (this->isShaderSupported) { glUniform1i(OGLRef.uniformPolyEnableTexture, GL_FALSE); + glUniform1i(OGLRef.uniformTexSingleBitAlpha, GL_FALSE); } else { @@ -3084,6 +3127,7 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT if (this->isShaderSupported) { glUniform1i(OGLRef.uniformPolyEnableTexture, GL_TRUE); + glUniform1i(OGLRef.uniformTexSingleBitAlpha, (params.texFormat != TEXMODE_A3I5 && params.texFormat != TEXMODE_A5I3) ? GL_TRUE : GL_FALSE); } else { @@ -3108,8 +3152,6 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT OGLRef.freeTextureIDs.pop(); glBindTexture(GL_TEXTURE_2D, (GLuint)this->currTexture->texid); - 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, (params.enableRepeatS ? (params.enableMirroredRepeatS ? OGLRef.stateTexMirroredRepeat : GL_REPEAT) : GL_CLAMP_TO_EDGE)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (params.enableRepeatT ? (params.enableMirroredRepeatT ? OGLRef.stateTexMirroredRepeat : GL_REPEAT) : GL_CLAMP_TO_EDGE)); @@ -3125,25 +3167,81 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT switch (this->_textureScalingFactor) { + case 1: + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (this->_textureSmooth) ? this->_deviceInfo.maxAnisotropy : 1.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + break; + } + case 2: { - this->TextureUpscale<2>(textureSrc, texWidth, texHeight); - textureSrc = this->_textureUpscaleBuffer; + if (this->_textureSmooth) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy); + + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + } + else + { + 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_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + } break; } case 4: { - this->TextureUpscale<4>(textureSrc, texWidth, texHeight); - textureSrc = this->_textureUpscaleBuffer; + if (this->_textureSmooth) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy); + + this->TextureUpscale<4>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + texWidth = currTexture->sizeX; + texHeight = currTexture->sizeY; + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + } + else + { + 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_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + this->TextureUpscale<4>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + } break; } default: break; } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); } else { @@ -3313,8 +3411,7 @@ Render3DError OpenGLRenderer_1_2::SetFramebufferSize(size_t w, size_t h) if (this->isMultisampledFBOSupported) { - GLint maxSamples = 0; - glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); + GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGColorID); glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h); @@ -3493,8 +3590,7 @@ Render3DError OpenGLRenderer_1_3::SetFramebufferSize(size_t w, size_t h) if (this->isMultisampledFBOSupported) { - GLint maxSamples = 0; - glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); + GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGColorID); glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h); @@ -3819,6 +3915,15 @@ Render3DError OpenGLRenderer_2_0::InitExtensions() std::set oglExtensionSet; this->GetExtensionSet(&oglExtensionSet); + // Get host GPU device properties + GLfloat maxAnisotropyOGL = 1.0f; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyOGL); + this->_deviceInfo.maxAnisotropy = maxAnisotropyOGL; + + GLint maxSamplesOGL = 0; + glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamplesOGL); + this->_deviceInfo.maxSamples = (u8)maxSamplesOGL; + // Initialize OpenGL this->InitTables(); @@ -3859,6 +3964,8 @@ Render3DError OpenGLRenderer_2_0::InitExtensions() { this->DestroyGeometryProgram(); this->isShaderSupported = false; + this->_deviceInfo.isEdgeMarkSupported = false; + this->_deviceInfo.isFogSupported = false; } this->isVBOSupported = true; @@ -4694,10 +4801,12 @@ Render3DError OpenGLRenderer_2_0::SetupTexture(const POLY &thePoly, bool enableT if (params.texFormat == TEXMODE_NONE || !enableTexturing) { glUniform1i(OGLRef.uniformPolyEnableTexture, GL_FALSE); + glUniform1i(OGLRef.uniformTexSingleBitAlpha, GL_FALSE); return OGLERROR_NOERR; } glUniform1i(OGLRef.uniformPolyEnableTexture, GL_TRUE); + glUniform1i(OGLRef.uniformTexSingleBitAlpha, (params.texFormat != TEXMODE_A3I5 && params.texFormat != TEXMODE_A5I3) ? GL_TRUE : GL_FALSE); TexCacheItem *newTexture = TexCache_SetTexture(TexFormat_32bpp, thePoly.texParam, thePoly.texPalette); if(newTexture != this->currTexture) @@ -4717,8 +4826,6 @@ Render3DError OpenGLRenderer_2_0::SetupTexture(const POLY &thePoly, bool enableT OGLRef.freeTextureIDs.pop(); glBindTexture(GL_TEXTURE_2D, (GLuint)this->currTexture->texid); - 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, (params.enableRepeatS ? (params.enableMirroredRepeatS ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (params.enableRepeatT ? (params.enableMirroredRepeatT ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE)); @@ -4734,25 +4841,81 @@ Render3DError OpenGLRenderer_2_0::SetupTexture(const POLY &thePoly, bool enableT switch (this->_textureScalingFactor) { + case 1: + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (this->_textureSmooth) ? this->_deviceInfo.maxAnisotropy : 1.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + break; + } + case 2: { - this->TextureUpscale<2>(textureSrc, texWidth, texHeight); - textureSrc = this->_textureUpscaleBuffer; + if (this->_textureSmooth) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy); + + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + } + else + { + 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_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + } break; } case 4: { - this->TextureUpscale<4>(textureSrc, texWidth, texHeight); - textureSrc = this->_textureUpscaleBuffer; + if (this->_textureSmooth) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy); + + this->TextureUpscale<4>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + texWidth = currTexture->sizeX; + texHeight = currTexture->sizeY; + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + } + else + { + 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_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + this->TextureUpscale<4>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + } break; } default: break; } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); } else { diff --git a/desmume/src/OGLRender.h b/desmume/src/OGLRender.h index ce38a9d82..a80553f53 100644 --- a/desmume/src/OGLRender.h +++ b/desmume/src/OGLRender.h @@ -389,7 +389,7 @@ struct OGLPolyStates union { - struct { GLubyte texSizeS, texSizeT, texParamPad[2]; }; + struct { GLubyte texSizeS, texSizeT, texSingleBitAlpha, texParamPad[1]; }; GLubyte texParam[4]; }; }; @@ -481,6 +481,7 @@ struct OGLRenderRef GLint uniformPolyEnableTexture; GLint uniformPolyEnableFog; + GLint uniformTexSingleBitAlpha; GLint uniformPolyStateIndex; diff --git a/desmume/src/OGLRender_3_2.cpp b/desmume/src/OGLRender_3_2.cpp index ad84754eb..85d492dc7 100644 --- a/desmume/src/OGLRender_3_2.cpp +++ b/desmume/src/OGLRender_3_2.cpp @@ -122,6 +122,7 @@ static const char *GeometryVtxShader_150 = {"\ flat out uint polySetNewDepthForTranslucent;\n\ flat out uint polyMode;\n\ flat out uint polyID;\n\ + flat out uint texSingleBitAlpha;\n\ \n\ void main() \n\ { \n\ @@ -138,6 +139,7 @@ static const char *GeometryVtxShader_150 = {"\ polySetNewDepthForTranslucent = polyStateFlags[3];\n\ polyMode = polyStateValues[1];\n\ polyID = polyStateValues[2];\n\ + texSingleBitAlpha = polyStateTexParams[2];\n\ \n\ mat2 texScaleMtx = mat2( vec2(polyTexScale.x, 0.0), \n\ vec2( 0.0, polyTexScale.y)); \n\ @@ -163,6 +165,7 @@ static const char *GeometryFragShader_150 = {"\ flat in uint polySetNewDepthForTranslucent;\n\ flat in uint polyMode;\n\ flat in uint polyID;\n\ + flat in uint texSingleBitAlpha;\n\ \n\ layout (std140) uniform RenderStates\n\ {\n\ @@ -202,6 +205,20 @@ static const char *GeometryFragShader_150 = {"\ void main() \n\ { \n\ vec4 mainTexColor = bool(polyEnableTexture) ? texture(texRenderObject, vtxTexCoord) : vec4(1.0, 1.0, 1.0, 1.0);\n\ + \n\ + if (bool(texSingleBitAlpha))\n\ + {\n\ + if (mainTexColor.a < 0.500)\n\ + {\n\ + mainTexColor.a = 0.0;\n\ + }\n\ + else\n\ + {\n\ + mainTexColor.rgb = mainTexColor.rgb / mainTexColor.a;\n\ + mainTexColor.a = 1.0;\n\ + }\n\ + }\n\ + \n\ vec4 newFragColor = mainTexColor * vtxColor; \n\ \n\ if (polyMode == 1u) \n\ @@ -222,10 +239,10 @@ static const char *GeometryFragShader_150 = {"\ } \n\ } \n\ \n\ - if (newFragColor.a == 0.0 || (state.enableAlphaTest && newFragColor.a < state.alphaTestRef)) \n\ - { \n\ - discard; \n\ - } \n\ + if (newFragColor.a < 0.001 || (state.enableAlphaTest && newFragColor.a < state.alphaTestRef))\n\ + {\n\ + discard;\n\ + }\n\ \n\ float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w; \n\ // hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\ @@ -502,6 +519,18 @@ Render3DError OpenGLRenderer_3_2::InitExtensions() std::set oglExtensionSet; this->GetExtensionSet(&oglExtensionSet); + // Get host GPU device properties + GLfloat maxAnisotropyOGL = 1.0f; + 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; + // Initialize OpenGL this->InitTables(); @@ -955,8 +984,7 @@ Render3DError OpenGLRenderer_3_2::CreateMultisampledFBO() // 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 = 0; - glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; if (maxSamples < 2) { @@ -1387,6 +1415,7 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D &engine) polyStates[i].polyID = polyAttr.polygonID; polyStates[i].texSizeS = texParams.sizeS; polyStates[i].texSizeT = texParams.sizeT; + polyStates[i].texSingleBitAlpha = (texParams.texFormat != TEXMODE_A3I5 && texParams.texFormat != TEXMODE_A5I3) ? GL_TRUE : GL_FALSE; for (size_t j = 0; j < polyType; j++) { @@ -1684,8 +1713,6 @@ Render3DError OpenGLRenderer_3_2::SetupTexture(const POLY &thePoly, bool enableT OGLRef.freeTextureIDs.pop(); glBindTexture(GL_TEXTURE_2D, (GLuint)this->currTexture->texid); - 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, (params.enableRepeatS ? (params.enableMirroredRepeatS ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (params.enableRepeatT ? (params.enableMirroredRepeatT ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE)); @@ -1701,25 +1728,81 @@ Render3DError OpenGLRenderer_3_2::SetupTexture(const POLY &thePoly, bool enableT switch (this->_textureScalingFactor) { + case 1: + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (this->_textureSmooth) ? this->_deviceInfo.maxAnisotropy : 1.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + break; + } + case 2: { - this->TextureUpscale<2>(textureSrc, texWidth, texHeight); - textureSrc = this->_textureUpscaleBuffer; + if (this->_textureSmooth) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy); + + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + } + else + { + 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_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + } break; } case 4: { - this->TextureUpscale<4>(textureSrc, texWidth, texHeight); - textureSrc = this->_textureUpscaleBuffer; + if (this->_textureSmooth) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy); + + this->TextureUpscale<4>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + texWidth = currTexture->sizeX; + texHeight = currTexture->sizeY; + this->TextureUpscale<2>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); + } + else + { + 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_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + this->TextureUpscale<4>(textureSrc, texWidth, texHeight); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer); + } break; } default: break; } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc); } else { @@ -1779,8 +1862,7 @@ Render3DError OpenGLRenderer_3_2::SetFramebufferSize(size_t w, size_t h) if (this->isMultisampledFBOSupported) { - GLint maxSamples = 0; - glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + GLint maxSamples = (GLint)this->_deviceInfo.maxSamples; glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGColorID); glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, w, h); diff --git a/desmume/src/gfx3d.cpp b/desmume/src/gfx3d.cpp index 1ece673b0..52d62a590 100644 --- a/desmume/src/gfx3d.cpp +++ b/desmume/src/gfx3d.cpp @@ -2336,7 +2336,9 @@ void gfx3d_VBlankEndSignal(bool skipFrame) if (CommonSettings.showGpu.main) { CurrentRenderer->SetRenderNeedsFinish(true); - CurrentRenderer->SetTextureProcessingProperties(CommonSettings.GFX3D_Renderer_TextureDeposterize, CommonSettings.GFX3D_Renderer_TextureScalingFactor); + CurrentRenderer->SetTextureProcessingProperties(CommonSettings.GFX3D_Renderer_TextureScalingFactor, + CommonSettings.GFX3D_Renderer_TextureDeposterize, + CommonSettings.GFX3D_Renderer_TextureSmoothing); CurrentRenderer->Render(gfx3d); } else diff --git a/desmume/src/rasterize.cpp b/desmume/src/rasterize.cpp index ff9e042a5..4d3cecabe 100644 --- a/desmume/src/rasterize.cpp +++ b/desmume/src/rasterize.cpp @@ -1165,8 +1165,14 @@ GPU3DInterface gpu3DRasterize = { SoftRasterizerRenderer::SoftRasterizerRenderer() { - _renderID = RENDERID_SOFTRASTERIZER; - _renderName = "SoftRasterizer"; + _deviceInfo.renderID = RENDERID_SOFTRASTERIZER; + _deviceInfo.renderName = "SoftRasterizer"; + _deviceInfo.isTexturingSupported = true; + _deviceInfo.isEdgeMarkSupported = true; + _deviceInfo.isFogSupported = true; + _deviceInfo.isTextureSmoothingSupported = false; + _deviceInfo.maxAnisotropy = 1.0f; + _deviceInfo.maxSamples = 0; _debug_drawClippedUserPoly = -1; clippedPolys = clipper.clippedPolys = new GFX3D_Clipper::TClippedPoly[POLYLIST_SIZE*2]; diff --git a/desmume/src/render3D.cpp b/desmume/src/render3D.cpp index 396214e01..72b795710 100644 --- a/desmume/src/render3D.cpp +++ b/desmume/src/render3D.cpp @@ -265,8 +265,14 @@ void Render3D::operator delete(void *ptr) Render3D::Render3D() { - _renderID = RENDERID_NULL; - _renderName = "None"; + _deviceInfo.renderID = RENDERID_NULL; + _deviceInfo.renderName = "None"; + _deviceInfo.isTexturingSupported = false; + _deviceInfo.isEdgeMarkSupported = false; + _deviceInfo.isFogSupported = false; + _deviceInfo.isTextureSmoothingSupported = false; + _deviceInfo.maxAnisotropy = 1.0f; + _deviceInfo.maxSamples = 0; static bool needTableInit = true; @@ -292,6 +298,7 @@ Render3D::Render3D() _willFlushFramebufferRGBA5551 = true; _textureScalingFactor = 1; + _textureSmooth = false; _textureDeposterizeBuffer = NULL; _textureUpscaleBuffer = NULL; @@ -303,14 +310,19 @@ Render3D::~Render3D() // Do nothing. } +const Render3DDeviceInfo& Render3D::GetDeviceInfo() +{ + return this->_deviceInfo; +} + RendererID Render3D::GetRenderID() { - return this->_renderID; + return this->_deviceInfo.renderID; } std::string Render3D::GetName() { - return this->_renderName; + return this->_deviceInfo.renderName; } FragmentColor* Render3D::GetFramebuffer() @@ -381,7 +393,7 @@ void Render3D::SetRenderNeedsFinish(const bool renderNeedsFinish) this->_renderNeedsFinish = renderNeedsFinish; } -void Render3D::SetTextureProcessingProperties(bool willDeposterize, size_t scalingFactor) +void Render3D::SetTextureProcessingProperties(size_t scalingFactor, bool willDeposterize, bool willSmooth) { const bool isScaleValid = ( (scalingFactor == 2) || (scalingFactor == 4) ); const size_t newScalingFactor = (isScaleValid) ? scalingFactor : 1; @@ -416,6 +428,13 @@ void Render3D::SetTextureProcessingProperties(bool willDeposterize, size_t scali needTexCacheReset = true; } + if (willSmooth != this->_textureSmooth) + { + this->_textureSmooth = willSmooth; + + needTexCacheReset = true; + } + if (needTexCacheReset) { TexCache_Reset(); diff --git a/desmume/src/render3D.h b/desmume/src/render3D.h index d3fbbbeb5..82c7d9bf6 100644 --- a/desmume/src/render3D.h +++ b/desmume/src/render3D.h @@ -98,11 +98,24 @@ struct FragmentAttributesBuffer void SetAll(const FragmentAttributes &attr); }; +struct Render3DDeviceInfo +{ + RendererID renderID; + std::string renderName; + + bool isTexturingSupported; + bool isEdgeMarkSupported; + bool isFogSupported; + bool isTextureSmoothingSupported; + + float maxAnisotropy; + u8 maxSamples; +}; + class Render3D { protected: - RendererID _renderID; - std::string _renderName; + Render3DDeviceInfo _deviceInfo; size_t _framebufferWidth; size_t _framebufferHeight; @@ -116,6 +129,7 @@ protected: bool _willFlushFramebufferRGBA5551; size_t _textureScalingFactor; + bool _textureSmooth; u32 *_textureDeposterizeBuffer; u32 *_textureUpscaleBuffer; @@ -147,6 +161,7 @@ public: Render3D(); ~Render3D(); + const Render3DDeviceInfo& GetDeviceInfo(); RendererID GetRenderID(); std::string GetName(); @@ -179,7 +194,7 @@ public: bool GetRenderNeedsFinish() const; void SetRenderNeedsFinish(const bool renderNeedsFinish); - void SetTextureProcessingProperties(bool willDeposterize, size_t scalingFactor); + void SetTextureProcessingProperties(size_t scalingFactor, bool willDeposterize, bool willSmooth); }; #ifdef ENABLE_SSE2 diff --git a/desmume/src/texcache.cpp b/desmume/src/texcache.cpp index 981925895..73c377a77 100644 --- a/desmume/src/texcache.cpp +++ b/desmume/src/texcache.cpp @@ -397,16 +397,21 @@ public: //============================================================================ //Texture conversion - //============================================================================ + //============================================================================ + + // Whenever a 1-bit alpha or no-alpha texture is unpacked (this means any texture + // format that is not A3I5 or A5I3), set all transparent pixels to 0 so that 3D + // renderers can assume that the transparent color is 0 during texture sampling. const u8 opaqueColor = (TEXFORMAT == TexFormat_32bpp) ? 0xFF : 0x1F; const u8 palZeroTransparent = ( 1 - ((format>>29) & 1) ) * opaqueColor; switch (newitem->mode) { - case TEXMODE_A3I5: + case TEXMODE_A3I5: { - for(int j=0;j>2)&0x3; - c = pal[bits]; - *dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor); - - bits = ((*adr)>>4)&0x3; - c = pal[bits]; - *dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor); - - bits = ((*adr)>>6)&0x3; - c = pal[bits]; - *dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor); - - adr++; + adr = ms.items[j].ptr; + for(u32 x = 0; x < ms.items[j].len; x++) + { + u8 bits; + u16 c; + + bits = (*adr)&0x3; + c = pal[bits]; + *dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor); + + bits = ((*adr)>>2)&0x3; + c = pal[bits]; + *dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor); + + bits = ((*adr)>>4)&0x3; + c = pal[bits]; + *dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor); + + bits = ((*adr)>>6)&0x3; + c = pal[bits]; + *dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor); + + adr++; + } + } + } + else + { + for(int j=0;j>2)&0x3; + c = pal[bits]; + *dwdst++ = CONVERT(c,opaqueColor); + + bits = ((*adr)>>4)&0x3; + c = pal[bits]; + *dwdst++ = CONVERT(c,opaqueColor); + + bits = ((*adr)>>6)&0x3; + c = pal[bits]; + *dwdst++ = CONVERT(c,opaqueColor); + + adr++; + } } } break; } - case TEXMODE_I4: + + case TEXMODE_I4: { - for(int j=0;j>4); - c = pal[bits]; - *dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor); - adr++; + adr = ms.items[j].ptr; + for(u32 x = 0; x < ms.items[j].len; x++) + { + u8 bits; + u16 c; + + bits = (*adr)&0xF; + c = pal[bits]; + *dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor); + + bits = ((*adr)>>4); + c = pal[bits]; + *dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor); + adr++; + } + } + } + else + { + for(int j=0;j>4); + c = pal[bits]; + *dwdst++ = CONVERT(c,opaqueColor); + adr++; + } } } break; } - case TEXMODE_I8: + + case TEXMODE_I8: { - for(int j=0;j> 1) & 0x00FF0000 ) | ( (((tmp_col[0] & 0x0000FF00) + (tmp_col[1] & 0x0000FF00)) >> 1) & 0x0000FF00 ) | 0x000000FF; - tmp_col[3] = 0xFFFFFF00; + tmp_col[3] = 0x00000000; #else tmp_col[2] = ( (((tmp_col[0] & 0x00FF00FF) + (tmp_col[1] & 0x00FF00FF)) >> 1) & 0x00FF00FF ) | ( (((tmp_col[0] & 0x0000FF00) + (tmp_col[1] & 0x0000FF00)) >> 1) & 0x0000FF00 ) | 0xFF000000; - tmp_col[3] = 0x00FFFFFF; + tmp_col[3] = 0x00000000; #endif break; @@ -634,9 +718,11 @@ public: } break; } - case TEXMODE_A5I3: + + case TEXMODE_A5I3: { - for(int j=0;j>1; + for(int x = 0; x < len; ++x) { u16 c = map[x]; - int alpha = ((c&0x8000)?opaqueColor:0); - *dwdst++ = CONVERT(c&0x7FFF,alpha); + *dwdst++ = (c & 0x8000) ? CONVERT(c&0x7FFF,opaqueColor) : 0; } } break;