From acf26e613da296073c7ec22600ea1841f23d859c Mon Sep 17 00:00:00 2001 From: s2s <12202580+standard-two-simplex@users.noreply.github.com> Date: Fri, 5 Mar 2021 18:42:30 +0100 Subject: [PATCH] Apply a vertex position or texture coordinate shift to approximate N64 rasterization rules --- .../GLSL/glsl_CombinerProgramBuilder.cpp | 22 ++++++++- .../GLSL/glsl_CombinerProgramBuilder.h | 1 + .../glsl_CombinerProgramUniformFactory.cpp | 45 +++++++++++++++++++ src/GraphicsDrawer.cpp | 16 ------- src/gDP.cpp | 9 ++++ src/gDP.h | 10 ++++- 6 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp index cd7bc611..6f0d3aff 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp +++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp @@ -267,6 +267,7 @@ public: ss << "# define IN in" << std::endl << "# define OUT out" << std::endl; m_part = ss.str(); } + m_part += "uniform lowp vec2 uVertexOffset; \n"; } }; @@ -512,6 +513,7 @@ public: m_part = " gl_Position.z /= 8.0; \n"; } m_part += + " gl_Position.xy += uVertexOffset * vec2(gl_Position.w); \n" " gl_Position.zw *= vec2(1024.0f); \n" "} \n" ; @@ -955,6 +957,7 @@ public: "uniform lowp vec2 uTexMirrorEn1; \n" "uniform lowp vec2 uTexClampEn0; \n" "uniform lowp vec2 uTexClampEn1; \n" + "uniform highp vec2 uTexCoordOffset; \n" "uniform lowp int uScreenSpaceTriangle; \n" "highp vec2 texCoord0; \n" "highp vec2 texCoord1; \n" @@ -2584,6 +2587,19 @@ public: }; +class ShaderFragmentCorrectTexCoords : public ShaderPart { +public: + ShaderFragmentCorrectTexCoords() { + m_part += + " highp vec2 mTexCoord0 = vTexCoord0 + vec2(0.0001); \n" + " highp vec2 mTexCoord1 = vTexCoord1 + vec2(0.0001); \n" + " mTexCoord0 += uTexCoordOffset; \n" + " mTexCoord1 += uTexCoordOffset; \n" + ; + } +}; + + class ShaderTextureEngine : public ShaderPart { public: @@ -2646,7 +2662,7 @@ public: ShaderFragmentTextureEngineTex0(const opengl::GLInfo _glinfo) { m_part = - "textureEngine0(vTexCoord0, tcData0); \n" + "textureEngine0(mTexCoord0, tcData0); \n" ; } }; @@ -2656,7 +2672,7 @@ public: ShaderFragmentTextureEngineTex1(const opengl::GLInfo _glinfo) { m_part = - "textureEngine1(vTexCoord1, tcData1); \n" + "textureEngine1(mTexCoord1, tcData1); \n" ; } }; @@ -2900,6 +2916,7 @@ graphics::CombinerProgram * CombinerProgramBuilder::buildCombinerProgram(Combine if (bUseTextures) { + m_fragmentCorrectTexCoords->write(ssShader); if (combinerInputs.usesTile(0)) { m_fragmentTextureEngineTex0->write(ssShader); @@ -3069,6 +3086,7 @@ CombinerProgramBuilder::CombinerProgramBuilder(const opengl::GLInfo & _glinfo, o , m_fragmentBlendMux(new ShaderFragmentBlendMux(_glinfo)) , m_fragmentReadTex0(new ShaderFragmentReadTex0(_glinfo)) , m_fragmentReadTex1(new ShaderFragmentReadTex1(_glinfo)) +, m_fragmentCorrectTexCoords(new ShaderFragmentCorrectTexCoords()) , m_fragmentTextureEngineTex0(new ShaderFragmentTextureEngineTex0(_glinfo)) , m_fragmentTextureEngineTex1(new ShaderFragmentTextureEngineTex1(_glinfo)) , m_fragmentReadTexCopyMode(new ShaderFragmentReadTexCopyMode(_glinfo)) diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h index d46a76bb..92698035 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h +++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h @@ -74,6 +74,7 @@ namespace glsl { ShaderPartPtr m_fragmentBlendMux; ShaderPartPtr m_fragmentReadTex0; ShaderPartPtr m_fragmentReadTex1; + ShaderPartPtr m_fragmentCorrectTexCoords; ShaderPartPtr m_fragmentTextureEngineTex0; ShaderPartPtr m_fragmentTextureEngineTex1; ShaderPartPtr m_fragmentReadTexCopyMode; diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp index 55e6633d..241c4859 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp +++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp @@ -216,6 +216,50 @@ private: iUniform uScreenSpaceTriangle; }; +class URasterInfo : public UniformGroup { +public: + URasterInfo(GLuint _program) { + LocateUniform(uVertexOffset); + LocateUniform(uTexCoordOffset); + } + + void update(bool _force) override { + const bool isNativeRes = config.frameBufferEmulation.nativeResFactor == 1 && config.video.multisampling == 0; + const bool isTexRect = dwnd().getDrawer().getDrawingState() == DrawingState::TexRect; + float scale[2] = { 0.0f, 0.0f }; + if (config.frameBufferEmulation.nativeResFactor != 0) { + scale[0] = scale[1] = static_cast(config.frameBufferEmulation.nativeResFactor); + } else { + scale[0] = dwnd().getScaleX(); + scale[1] = dwnd().getScaleY(); + } + /* At rasterization stage, the N64 places samples on the top left of the fragment while OpenGL */ + /* places them in the fragment center. As a result, a normal approach results in shifted texture */ + /* coordinates. In native resolution, this difference can be negated by shifting vertices by 0.5. */ + /* In higher resolutions, there are more samples than the game intends, so shifting is not very */ + /* effective. Still, an heuristic is applied to render texture rectangles as correctly as possible */ + /* in higher resolutions too. See issue #2324 for details. */ + const float vertexOffset = isNativeRes ? 0.5f : 0.0f; + float texCoordOffset[2] = { 0.0f, 0.0f }; + if (isTexRect && !isNativeRes) { + if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) { + texCoordOffset[0] = -0.5f * gDP.lastTexRectInfo.dsdx; + texCoordOffset[1] = -0.5f * gDP.lastTexRectInfo.dtdy; + } else { + texCoordOffset[0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? -0.5f / scale[0] : -1.0f + 0.5f / scale[0]) * gDP.lastTexRectInfo.dsdx; + texCoordOffset[1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? -0.5f / scale[1] : -1.0f + 0.5f / scale[1]) * gDP.lastTexRectInfo.dtdy; + } + } + + uVertexOffset.set(vertexOffset, vertexOffset, _force); + uTexCoordOffset.set(texCoordOffset[0], texCoordOffset[1], _force); + } + +private: + fv2Uniform uVertexOffset; + fv2Uniform uTexCoordOffset; +}; + class UFrameBufferInfo : public UniformGroup { public: @@ -1115,6 +1159,7 @@ void CombinerProgramUniformFactory::buildUniforms(GLuint _program, { _uniforms.emplace_back(new UNoiseTex(_program)); _uniforms.emplace_back(new UScreenSpaceTriangleInfo(_program)); + _uniforms.emplace_back(new URasterInfo(_program)); _uniforms.emplace_back(new UViewportInfo(_program)); if (!m_glInfo.isGLES2) { diff --git a/src/GraphicsDrawer.cpp b/src/GraphicsDrawer.cpp index 9b249d5c..baccfcbb 100644 --- a/src/GraphicsDrawer.cpp +++ b/src/GraphicsDrawer.cpp @@ -1315,15 +1315,6 @@ void GraphicsDrawer::drawTexturedRect(const TexturedRectParams & _params) texST[t].t0 = ult - gSP.textureTile[t]->fult; texST[t].t1 = lrt - gSP.textureTile[t]->fult; - if (uls > lrs) { - texST[t].s0 -= _params.dsdx * shiftScaleS; - texST[t].s1 -= _params.dsdx * shiftScaleS; - } - if (ult > lrt) { - texST[t].t0 -= _params.dtdy * shiftScaleT; - texST[t].t1 -= _params.dtdy * shiftScaleT; - } - if (cache.current[t]->frameBufferTexture != CachedTexture::fbNone) { texST[t].s0 = cache.current[t]->offsetS + texST[t].s0; texST[t].t0 = cache.current[t]->offsetT + texST[t].t0; @@ -1360,13 +1351,6 @@ void GraphicsDrawer::drawTexturedRect(const TexturedRectParams & _params) texST[t].s1 *= cache.current[t]->hdRatioS; texST[t].t1 *= cache.current[t]->hdRatioT; - if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) { - texST[t].s0 -= 0.5f; - texST[t].t0 -= 0.5f; - texST[t].s1 -= 0.5f; - texST[t].t1 -= 0.5f; - } - } } diff --git a/src/gDP.cpp b/src/gDP.cpp index 8ee1d844..0c5c231d 100644 --- a/src/gDP.cpp +++ b/src/gDP.cpp @@ -866,6 +866,15 @@ void gDPTextureRectangle(f32 ulx, f32 uly, f32 lrx, f32 lry, s32 tile, s16 s, s1 gDP.rectColor = gDPInfo::Color(); + gDP.lastTexRectInfo.ulx = ulx; + gDP.lastTexRectInfo.lrx = lrx; + gDP.lastTexRectInfo.uly = uly; + gDP.lastTexRectInfo.lry = lry; + gDP.lastTexRectInfo.s = !flip ? s : t; + gDP.lastTexRectInfo.t = !flip ? t : s; + gDP.lastTexRectInfo.dsdx = !flip ? dsdx : dtdy; + gDP.lastTexRectInfo.dtdy = !flip ? dtdy : dsdx; + GraphicsDrawer & drawer = dwnd().getDrawer(); GraphicsDrawer::TexturedRectParams params(ulx, uly, lrx, lry, dsdx, dtdy, s, t, flip, false, true, frameBufferList().getCurrent()); diff --git a/src/gDP.h b/src/gDP.h index 021c3dc3..a8458950 100644 --- a/src/gDP.h +++ b/src/gDP.h @@ -122,6 +122,13 @@ struct gDPScissor s16 xh, yh, xl, yl; }; +struct gDPTexrectInfo +{ + f32 ulx, lrx, uly, lry; + s16 s, t; + f32 dsdx, dtdy; +}; + struct gDPInfo { struct OtherMode @@ -255,7 +262,8 @@ struct gDPInfo u64 paletteCRC256; u32 half_1, half_2; - gDPLoadTileInfo loadInfo[512]; + gDPLoadTileInfo loadInfo[512]; + gDPTexrectInfo lastTexRectInfo; }; extern gDPInfo gDP;