From 772ed302887903263a9f39c3226ef8f305270366 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 9 Sep 2018 17:00:12 -0700 Subject: [PATCH 1/6] SoftGPU: Avoid extra lookup of old stencil. --- GPU/Software/Rasterizer.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index b0b266ee7b..93aadeaec8 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -484,9 +484,8 @@ static inline bool StencilTestPassed(u8 stencil) return true; } -static inline u8 ApplyStencilOp(int op, int x, int y) -{ - u8 old_stencil = GetPixelStencil(x, y); // TODO: Apply mask? +static inline u8 ApplyStencilOp(int op, u8 old_stencil) { + // TODO: Apply mask to reference or old stencil? u8 reference_stencil = gstate.getStencilTestRef(); // TODO: Apply mask? switch (op) { @@ -916,7 +915,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 Date: Sun, 9 Sep 2018 18:11:37 -0700 Subject: [PATCH 2/6] SoftGPU: Preserve stencil inside logic op func. Cleaning things up to make it easier to translate. --- GPU/Software/Rasterizer.cpp | 47 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 93aadeaec8..c1fa015d08 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -537,71 +537,71 @@ static inline u8 ApplyStencilOp(int op, u8 old_stencil) { return old_stencil; } -static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) -{ +static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) { + // All of the operations here intentionally preserve alpha/stencil. switch (op) { case GE_LOGIC_CLEAR: - new_color = 0; + new_color &= 0xFF000000; break; case GE_LOGIC_AND: - new_color = new_color & old_color; + new_color = new_color & (old_color | 0xFF000000); break; case GE_LOGIC_AND_REVERSE: - new_color = new_color & ~old_color; + new_color = new_color & (~old_color | 0xFF000000); break; case GE_LOGIC_COPY: - //new_color = new_color; + // No change to new_color. break; case GE_LOGIC_AND_INVERTED: - new_color = ~new_color & old_color; + new_color = (~new_color & (old_color & 0x00FFFFFF)) | (new_color & 0xFF000000); break; case GE_LOGIC_NOOP: - new_color = old_color; + new_color = (old_color & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_XOR: - new_color = new_color ^ old_color; + new_color = new_color ^ (old_color & 0x00FFFFFF); break; case GE_LOGIC_OR: - new_color = new_color | old_color; + new_color = new_color | (old_color & 0x00FFFFFF); break; case GE_LOGIC_NOR: - new_color = ~(new_color | old_color); + new_color = (~(new_color | old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_EQUIV: - new_color = ~(new_color ^ old_color); + new_color = (~(new_color ^ old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_INVERTED: - new_color = ~old_color; + new_color = (~old_color & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_OR_REVERSE: - new_color = new_color | ~old_color; + new_color = new_color | (~old_color & 0x00FFFFFF); break; case GE_LOGIC_COPY_INVERTED: - new_color = ~new_color; + new_color = (~new_color & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_OR_INVERTED: - new_color = ~new_color | old_color; + new_color = ((~new_color | old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_NAND: - new_color = ~(new_color & old_color); + new_color = (~(new_color & old_color) & 0x00FFFFFF) | (new_color & 0xFF000000); break; case GE_LOGIC_SET: - new_color = 0xFFFFFFFF; + new_color |= 0x00FFFFFF; break; } @@ -895,8 +895,7 @@ static inline Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4< template inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &color_in) { Vec4 prim_color = color_in; - // Depth range test - // TODO: Clear mode? + // Depth range test - applied in clear mode, if not through mode. if (!gstate.isModeThrough()) if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax()) return; @@ -905,14 +904,12 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 dst = Vec4::FromRGBA(old_color); - // ToRGBA() always automatically clamps. + // ToRGB() always automatically clamps. new_color = AlphaBlendingResult(prim_color, dst).ToRGB(); new_color |= stencil << 24; } else { @@ -973,8 +970,8 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 Date: Sun, 9 Sep 2018 20:09:48 -0700 Subject: [PATCH 3/6] GPU: Apply color test after doubling. --- GPU/Common/ShaderId.cpp | 2 +- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 25 +++++++++++------ GPU/GLES/FragmentShaderGeneratorGLES.cpp | 28 ++++++++++++++------ GPU/GLES/FragmentTestCacheGLES.cpp | 2 ++ GPU/Software/Rasterizer.cpp | 24 +++++++++-------- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 23 +++++++++++----- headless/Headless.cpp | 1 + 7 files changed, 70 insertions(+), 35 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 9a6d9afb82..83bbd08e3e 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -233,7 +233,7 @@ void ComputeFragmentShaderID(ShaderID *id_out) { bool enableFog = gstate.isFogEnabled() && !isModeThrough; bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue(); bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue(); - bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled(); + bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled() && gstate.getTextureFunction() == GE_TEXFUNC_MODULATE; bool doTextureProjection = (gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX && MatrixNeedsProjection(gstate.tgenMatrix)); bool doTextureAlpha = gstate.isTextureAlphaUsed(); bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT; diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index bd8e9d4e3c..1d489296e5 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -304,6 +304,11 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag } } if (enableColorTest) { + // Color doubling happens before the color test, but we try to optimize doubling when test is off. + if (enableColorDoubling) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } + if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. @@ -322,14 +327,14 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag if (colorTestFuncs[colorTestFunc][0] != '#') { const char *test = colorTestFuncs[colorTestFunc]; if (lang == HLSL_D3D11) { - WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); + WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(clamp(v.rgb, 0.0, 1.0));\n"); WRITE(p, " uint3 v_masked = v_scaled & u_alphacolormask.rgb;\n"); WRITE(p, " uint3 colorTestRef = u_alphacolorref.rgb & u_alphacolormask.rgb;\n"); // We have to test the components separately, or we get incorrect results. See #10629. WRITE(p, " if (v_masked.r %s colorTestRef.r && v_masked.g %s colorTestRef.g && v_masked.b %s colorTestRef.b) discard;\n", test, test, test); } else { // TODO: Use a texture to lookup bitwise ops instead? - WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n"); + WRITE(p, " float3 colortest = roundAndScaleTo255v(clamp(v.rgb, 0.0, 1.0));\n"); WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b)) clip(-1);\n", test, test, test); } } @@ -337,13 +342,17 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag WRITE(p, lang == HLSL_DX9 ? " clip(-1);\n" : " discard;\n"); } } - } - // Color doubling happens after the color test. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + if (replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } + } else { + // If there's no color test, we can potentially double and replace blend at once. + if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 4.0;\n"); + } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } } if (enableFog) { diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 19dc1ba37d..64b43d35f8 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -558,6 +558,14 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform } if (enableColorTest) { + // Color doubling happens before the color test, but we try to optimize doubling when test is off. + if (enableColorDoubling) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + if (g_Config.bFragmentTestCache && !colorTestAgainstZero) { + WRITE(p, " vScale256.rgb = vScale256.rgb * 2.0;\n"); + } + } + if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. @@ -576,7 +584,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " float gResult = %s(testtex, vec2(vScale256.g, 0)).g;\n", texture); WRITE(p, " float bResult = %s(testtex, vec2(vScale256.b, 0)).b;\n", texture); if (colorTestFunc == GE_COMP_EQUAL) { - // Equal means all parts must be equal. + // Equal means all parts must be equal (so discard if any is not.) WRITE(p, " if (rResult < 0.5 || gResult < 0.5 || bResult < 0.5) %s\n", discardStatement); } else { // Not equal means any part must be not equal. @@ -587,7 +595,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform if (colorTestFuncs[colorTestFunc][0] != '#') { if (bitwiseOps) { // Apparently GLES3 does not support vector bitwise ops. - WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); + WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(clamp(v.rgb, 0.0, 1.0));\n"); const char *maskedFragColor = "ivec3(v_scaled.r & u_alphacolormask.r, v_scaled.g & u_alphacolormask.g, v_scaled.b & u_alphacolormask.b)"; const char *maskedColorRef = "ivec3(int(u_alphacolorref.r) & u_alphacolormask.r, int(u_alphacolorref.g) & u_alphacolormask.g, int(u_alphacolorref.b) & u_alphacolormask.b)"; WRITE(p, " if (%s %s %s) %s\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef, discardStatement); @@ -600,13 +608,17 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " %s\n", discardStatement); } } - } - // Color doubling happens after the color test. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + if (replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } + } else { + // If there's no color test, we can potentially double and replace blend at once. + if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 4.0;\n"); + } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } } if (enableFog) { diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index ea482896a7..e870426ffd 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -81,6 +81,8 @@ void FragmentTestCacheGLES::BindTestTexture(int slot) { GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid); lastTexture_ = tex; render_->BindTexture(slot, tex); + // We only need to do this once for the texture. + render_->SetTextureSampler(slot, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); FragmentTestTexture item; item.lastFrame = gpuStats.numFlips; item.texture = tex; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index c1fa015d08..9fe9a9a6ef 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -622,7 +622,13 @@ static inline Vec4 GetTextureFunctionOutput(const Vec4& prim_color, co // We can be accurate up to 24 bit integers, should be enough. const __m128 p = _mm_cvtepi32_ps(prim_color.ivec); const __m128 t = _mm_cvtepi32_ps(texcolor.ivec); - out_rgb.ivec = _mm_cvtps_epi32(_mm_div_ps(_mm_mul_ps(p, t), _mm_set_ps1(255.0f))); + const __m128 b = _mm_mul_ps(p, t); + if (gstate.isColorDoublingEnabled()) { + // We double right here, only for modulate. Other tex funcs do not color double. + out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(2.0f / 255.0f))); + } else { + out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(1.0f / 255.0f))); + } if (rgba) { return Vec4(out_rgb.ivec); @@ -630,7 +636,11 @@ static inline Vec4 GetTextureFunctionOutput(const Vec4& prim_color, co out_a = prim_color.a(); } #else - out_rgb = prim_color.rgb() * texcolor.rgb() / 255; + if (gstate.isColorDoublingEnabled()) { + out_rgb = (prim_color.rgb() * texcolor.rgb() * 2) / 255; + } else { + out_rgb = prim_color.rgb() * texcolor.rgb() / 255; + } out_a = (rgba) ? (prim_color.a() * texcolor.a() / 255) : prim_color.a(); #endif break; @@ -894,7 +904,7 @@ static inline Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4< template inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &color_in) { - Vec4 prim_color = color_in; + Vec4 prim_color = color_in.Clamp(0, 255); // Depth range test - applied in clear mode, if not through mode. if (!gstate.isModeThrough()) if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax()) @@ -935,14 +945,6 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 fogColor = Vec3::FromRGB(gstate.fogcolor); fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255; diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index d35ade99e5..13a1116291 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -375,6 +375,11 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } if (enableColorTest) { + // Color doubling happens before the color test, but we try to optimize doubling when test is off. + if (enableColorDoubling) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } + if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. // Have my doubts that this special case is actually worth it, but whatever. @@ -392,19 +397,23 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } else { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; if (colorTestFuncs[colorTestFunc][0] != '#') { - WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); + WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(clamp(v.rgb, 0.0, 1.0));\n"); WRITE(p, " if ((v_scaled & base.alphacolormask.rgb) %s (base.alphacolorref.rgb & base.alphacolormask.rgb)) %s\n", colorTestFuncs[colorTestFunc], discardStatement); } else { WRITE(p, " %s\n", discardStatement); } } - } - // Color doubling happens after the color test. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + if (replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } + } else { + // If there's no color test, we can potentially double and replace blend at once. + if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 4.0;\n"); + } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + } } if (enableFog) { diff --git a/headless/Headless.cpp b/headless/Headless.cpp index e786e08c3b..a099118f74 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -373,6 +373,7 @@ int main(int argc, const char* argv[]) g_Config.iSplineBezierQuality = 2; g_Config.bHighQualityDepth = true; g_Config.bMemStickInserted = true; + g_Config.bFragmentTestCache = true; #ifdef _WIN32 InitSysDirectories(); From 8eb011c22d8917ca8e726cbf29c4fa954e412fc1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 9 Sep 2018 20:27:39 -0700 Subject: [PATCH 4/6] GPU: Clamp after color doubling. This is needed for blending to be correct - it blends a clamped value. --- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 12 +++++++----- GPU/GLES/FragmentShaderGeneratorGLES.cpp | 10 ++++++---- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 10 ++++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index 1d489296e5..a18e39b5fc 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -306,7 +306,7 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag if (enableColorTest) { // Color doubling happens before the color test, but we try to optimize doubling when test is off. if (enableColorDoubling) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); } if (colorTestAgainstZero) { @@ -327,14 +327,14 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag if (colorTestFuncs[colorTestFunc][0] != '#') { const char *test = colorTestFuncs[colorTestFunc]; if (lang == HLSL_D3D11) { - WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(clamp(v.rgb, 0.0, 1.0));\n"); + WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); WRITE(p, " uint3 v_masked = v_scaled & u_alphacolormask.rgb;\n"); WRITE(p, " uint3 colorTestRef = u_alphacolorref.rgb & u_alphacolormask.rgb;\n"); // We have to test the components separately, or we get incorrect results. See #10629. WRITE(p, " if (v_masked.r %s colorTestRef.r && v_masked.g %s colorTestRef.g && v_masked.b %s colorTestRef.b) discard;\n", test, test, test); } else { // TODO: Use a texture to lookup bitwise ops instead? - WRITE(p, " float3 colortest = roundAndScaleTo255v(clamp(v.rgb, 0.0, 1.0));\n"); + WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n"); WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b)) clip(-1);\n", test, test, test); } } @@ -349,8 +349,10 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag } else { // If there's no color test, we can potentially double and replace blend at once. if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = clamp(v.rgb * 4.0, 0.0, 2.0);\n"); + } else if (enableColorDoubling) { + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } else if (replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } } diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 64b43d35f8..10a4a33181 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -560,7 +560,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform if (enableColorTest) { // Color doubling happens before the color test, but we try to optimize doubling when test is off. if (enableColorDoubling) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); if (g_Config.bFragmentTestCache && !colorTestAgainstZero) { WRITE(p, " vScale256.rgb = vScale256.rgb * 2.0;\n"); } @@ -595,7 +595,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform if (colorTestFuncs[colorTestFunc][0] != '#') { if (bitwiseOps) { // Apparently GLES3 does not support vector bitwise ops. - WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(clamp(v.rgb, 0.0, 1.0));\n"); + WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); const char *maskedFragColor = "ivec3(v_scaled.r & u_alphacolormask.r, v_scaled.g & u_alphacolormask.g, v_scaled.b & u_alphacolormask.b)"; const char *maskedColorRef = "ivec3(int(u_alphacolorref.r) & u_alphacolormask.r, int(u_alphacolorref.g) & u_alphacolormask.g, int(u_alphacolorref.b) & u_alphacolormask.b)"; WRITE(p, " if (%s %s %s) %s\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef, discardStatement); @@ -615,8 +615,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform } else { // If there's no color test, we can potentially double and replace blend at once. if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = clamp(v.rgb * 4.0, 0.0, 2.0);\n"); + } else if (enableColorDoubling) { + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } else if (replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } } diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 13a1116291..57b341d377 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -377,7 +377,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { if (enableColorTest) { // Color doubling happens before the color test, but we try to optimize doubling when test is off. if (enableColorDoubling) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); } if (colorTestAgainstZero) { @@ -397,7 +397,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } else { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; if (colorTestFuncs[colorTestFunc][0] != '#') { - WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(clamp(v.rgb, 0.0, 1.0));\n"); + WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); WRITE(p, " if ((v_scaled & base.alphacolormask.rgb) %s (base.alphacolorref.rgb & base.alphacolormask.rgb)) %s\n", colorTestFuncs[colorTestFunc], discardStatement); } else { WRITE(p, " %s\n", discardStatement); @@ -410,8 +410,10 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } else { // If there's no color test, we can potentially double and replace blend at once. if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 4.0;\n"); - } else if (enableColorDoubling || replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = clamp(v.rgb * 4.0, 0.0, 2.0);\n"); + } else if (enableColorDoubling) { + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } else if (replaceBlend == REPLACE_BLEND_2X_SRC) { WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } } From 022670d8820a0f6cace4b9a0f3ad13903418f739 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 9 Sep 2018 21:11:51 -0700 Subject: [PATCH 5/6] GPU: Apply fog before color test. Hardware tests confirm the fog result is what's color tested. --- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 34 +++++++----------- GPU/GLES/FragmentShaderGeneratorGLES.cpp | 38 +++++++------------- GPU/Software/Rasterizer.cpp | 25 ++++++------- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 35 +++++++----------- 4 files changed, 52 insertions(+), 80 deletions(-) diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index a18e39b5fc..344f6c5542 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -268,6 +268,11 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag WRITE(p, " float4 v = p;\n"); break; } } + + if (enableColorDoubling) { + // This happens before fog is applied. + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } } else { // No texture mapping WRITE(p, " float4 v = In.v_color0 %s;\n", secondary); @@ -303,12 +308,13 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag } } } - if (enableColorTest) { - // Color doubling happens before the color test, but we try to optimize doubling when test is off. - if (enableColorDoubling) { - WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); - } + if (enableFog) { + WRITE(p, " float fogCoef = clamp(In.v_fogdepth, 0.0, 1.0);\n"); + WRITE(p, " v = lerp(float4(u_fogcolor, v.a), v, fogCoef);\n"); + } + + if (enableColorTest) { if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. @@ -342,24 +348,10 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag WRITE(p, lang == HLSL_DX9 ? " clip(-1);\n" : " discard;\n"); } } - - if (replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } - } else { - // If there's no color test, we can potentially double and replace blend at once. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = clamp(v.rgb * 4.0, 0.0, 2.0);\n"); - } else if (enableColorDoubling) { - WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); - } else if (replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } } - if (enableFog) { - WRITE(p, " float fogCoef = clamp(In.v_fogdepth, 0.0, 1.0);\n"); - WRITE(p, " v = lerp(float4(u_fogcolor, v.a), v, fogCoef);\n"); + if (replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 10a4a33181..1b063aff6a 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -500,11 +500,22 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " vec4 v = p;\n"); break; } } + + if (enableColorDoubling) { + // This happens before fog is applied. + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } } else { // No texture mapping WRITE(p, " vec4 v = v_color0 %s;\n", secondary); } + if (enableFog) { + WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); + WRITE(p, " v = mix(vec4(u_fogcolor, v.a), v, fogCoef);\n"); + // WRITE(p, " v.x = v_depth;\n"); + } + // Texture access is at half texels [0.5/256, 255.5/256], but colors are normalized [0, 255]. // So we have to scale to account for the difference. std::string alphaTestXCoord = "0"; @@ -558,14 +569,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform } if (enableColorTest) { - // Color doubling happens before the color test, but we try to optimize doubling when test is off. - if (enableColorDoubling) { - WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); - if (g_Config.bFragmentTestCache && !colorTestAgainstZero) { - WRITE(p, " vScale256.rgb = vScale256.rgb * 2.0;\n"); - } - } - if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. @@ -608,25 +611,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " %s\n", discardStatement); } } - - if (replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } - } else { - // If there's no color test, we can potentially double and replace blend at once. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = clamp(v.rgb * 4.0, 0.0, 2.0);\n"); - } else if (enableColorDoubling) { - WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); - } else if (replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } } - if (enableFog) { - WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); - WRITE(p, " v = mix(vec4(u_fogcolor, v.a), v, fogCoef);\n"); - // WRITE(p, " v.x = v_depth;\n"); + if (replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 9fe9a9a6ef..044f1c399f 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -910,14 +910,23 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 gstate.getDepthRangeMax()) return; - if (gstate.isColorTestEnabled() && !clearMode) - if (!ColorTestPassed(prim_color.rgb())) - return; - if (gstate.isAlphaTestEnabled() && !clearMode) if (!AlphaTestPassed(prim_color.a())) return; + // Fog is applied prior to color test. + if (gstate.isFogEnabled() && !gstate.isModeThrough() && !clearMode) { + Vec3 fogColor = Vec3::FromRGB(gstate.fogcolor); + fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255; + prim_color.r() = fogColor.r(); + prim_color.g() = fogColor.g(); + prim_color.b() = fogColor.b(); + } + + if (gstate.isColorTestEnabled() && !clearMode) + if (!ColorTestPassed(prim_color.rgb())) + return; + // In clear mode, it uses the alpha color as stencil. u8 stencil = clearMode ? prim_color.a() : GetPixelStencil(p.x, p.y); if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) { @@ -945,14 +954,6 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 fogColor = Vec3::FromRGB(gstate.fogcolor); - fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255; - prim_color.r() = fogColor.r(); - prim_color.g() = fogColor.g(); - prim_color.b() = fogColor.b(); - } - const u32 old_color = GetPixelColor(p.x, p.y); u32 new_color; diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 57b341d377..8a5831aabb 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -339,6 +339,11 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " vec4 v = p;\n"); break; } } + + if (enableColorDoubling) { + // This happens before fog is applied. + WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); + } } else { // No texture mapping WRITE(p, " vec4 v = v_color0 %s;\n", secondary); @@ -374,12 +379,13 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } } - if (enableColorTest) { - // Color doubling happens before the color test, but we try to optimize doubling when test is off. - if (enableColorDoubling) { - WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); - } + if (enableFog) { + WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); + WRITE(p, " v = mix(vec4(base.fogcolor, v.a), v, fogCoef);\n"); + // WRITE(p, " v.x = v_depth;\n"); + } + if (enableColorTest) { if (colorTestAgainstZero) { // When testing against 0 (common), we can avoid some math. // Have my doubts that this special case is actually worth it, but whatever. @@ -403,25 +409,10 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " %s\n", discardStatement); } } - - if (replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } - } else { - // If there's no color test, we can potentially double and replace blend at once. - if (enableColorDoubling && replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = clamp(v.rgb * 4.0, 0.0, 2.0);\n"); - } else if (enableColorDoubling) { - WRITE(p, " v.rgb = clamp(v.rgb * 2.0, 0.0, 1.0);\n"); - } else if (replaceBlend == REPLACE_BLEND_2X_SRC) { - WRITE(p, " v.rgb = v.rgb * 2.0;\n"); - } } - if (enableFog) { - WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); - WRITE(p, " v = mix(vec4(base.fogcolor, v.a), v, fogCoef);\n"); - // WRITE(p, " v.x = v_depth;\n"); + if (replaceBlend == REPLACE_BLEND_2X_SRC) { + WRITE(p, " v.rgb = v.rgb * 2.0;\n"); } if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) { From 817b5d7c1c9cc0cb1dd8ff932271dd997fa9bbbf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 9 Sep 2018 21:12:17 -0700 Subject: [PATCH 6/6] SoftGPU: Support fog on 3D rectangles. --- GPU/Software/Clipper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 851747807d..1ab91015eb 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -157,7 +157,7 @@ void ProcessRect(const VertexData& v0, const VertexData& v1) // Color and depth values of second vertex are used for the whole rectangle buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0; buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; - buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth = 1.0f; + buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth; VertexData* topleft = &buf[0]; VertexData* topright = &buf[1];