From 83d82dcf6612328d23d74367a9db38f20823181a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jun 2026 12:22:11 +0200 Subject: [PATCH] Shader depal: Don't generate a big switch in the shader, generate specialized shaders instead. --- Common/GPU/ShaderWriter.cpp | 1 - GPU/Common/FragmentShaderGenerator.cpp | 119 +++++++++++++------------ GPU/Common/ShaderId.cpp | 25 ++++-- GPU/Common/ShaderId.h | 2 +- GPU/Common/ShaderUniforms.cpp | 2 +- GPU/Common/TextureCacheCommon.cpp | 4 +- GPU/GLES/ShaderManagerGLES.cpp | 2 +- GPU/GPUState.h | 2 +- 8 files changed, 88 insertions(+), 69 deletions(-) diff --git a/Common/GPU/ShaderWriter.cpp b/Common/GPU/ShaderWriter.cpp index e49ad937b2..25f7537ebd 100644 --- a/Common/GPU/ShaderWriter.cpp +++ b/Common/GPU/ShaderWriter.cpp @@ -9,7 +9,6 @@ const char * const vulkan_glsl_preamble_fs = "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n" -"#extension GL_ARB_conservative_depth : enable\n" "#extension GL_ARB_shader_image_load_store : enable\n" "#define splat3(x) vec3(x)\n" "#define DISCARD discard\n" diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 5a860e87f9..df562f2da1 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -70,6 +70,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool texture3D = id.Bit(FS_BIT_3D_TEXTURE); bool arrayTexture = id.Bit(FS_BIT_SAMPLE_ARRAY_TEXTURE); + bool forceDepthWritesOff = id.Bit(FS_BIT_DEPTH_TEST_NEVER); + bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL) && !forceDepthWritesOff; ReplaceAlphaType stencilToAlpha = static_cast(id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2)); @@ -87,6 +89,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (gl_extensions.OES_texture_3D && texture3D) { extensions.push_back("#extension GL_OES_texture_3D: enable"); } + if (useDiscardStencilBugWorkaround) { + // The only use of layout (depth_...) + extensions.push_back("#extension GL_ARB_conservative_depth : enable\n"); + } } ShaderWriterFlags flags = ShaderWriterFlags::NONE; @@ -129,6 +135,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } ShaderDepalMode shaderDepalMode = (ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2); + GEBufferFormat shaderDepalFmt = (GEBufferFormat)(id.Bits(FS_BIT_SHADER_DEPAL_FORMAT, 3)); if (texture3D) { shaderDepalMode = ShaderDepalMode::OFF; } @@ -159,9 +166,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu shading = doFlatShading ? "flat" : ""; } - bool forceDepthWritesOff = id.Bit(FS_BIT_DEPTH_TEST_NEVER); - - bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL) && !forceDepthWritesOff; GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4); GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4); @@ -689,74 +693,77 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " } else {\n"); WRITE(p, " uv_round = uv;\n"); WRITE(p, " }\n"); + p.C(" highp vec4 t = ").SampleTexture2D("tex", "uv_round").C(";\n"); p.C(" highp vec4 t1 = ").SampleTexture2DOffset("tex", "uv_round", 1, 0).C(";\n"); p.C(" highp vec4 t2 = ").SampleTexture2DOffset("tex", "uv_round", 0, 1).C(";\n"); p.C(" highp vec4 t3 = ").SampleTexture2DOffset("tex", "uv_round", 1, 1).C(";\n"); + WRITE(p, " uint depalMask = (u_depal_mask_shift_off_fmt & 0xFFu);\n"); WRITE(p, " uint depalShift = (u_depal_mask_shift_off_fmt >> 0x8u) & 0xFFu;\n"); WRITE(p, " uint depalOffset = ((u_depal_mask_shift_off_fmt >> 0x10u) & 0xFFu) << 0x4u;\n"); WRITE(p, " uint depalFmt = (u_depal_mask_shift_off_fmt >> 0x18u) & 0x3u;\n"); WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n"); - WRITE(p, " switch (int(depalFmt)) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. - WRITE(p, " case 0:\n"); // 565 - WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); - WRITE(p, " index0 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " if (bilinear) {\n"); - WRITE(p, " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); - WRITE(p, " index1 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); - WRITE(p, " index2 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); - WRITE(p, " index3 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " }\n"); - WRITE(p, " break;\n"); - WRITE(p, " case 1:\n"); // 5551 - WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); - WRITE(p, " index0 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " if (bilinear) {\n"); - WRITE(p, " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); - WRITE(p, " index1 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); - WRITE(p, " index2 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); - WRITE(p, " index3 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); - WRITE(p, " }\n"); - WRITE(p, " break;\n"); - WRITE(p, " case 2:\n"); // 4444 - WRITE(p, " col = uvec4(t.rgba * 15.99);\n"); - WRITE(p, " index0 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); - WRITE(p, " if (bilinear) {\n"); - WRITE(p, " col = uvec4(t1.rgba * 15.99);\n"); - WRITE(p, " index1 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); - WRITE(p, " col = uvec4(t2.rgba * 15.99);\n"); - WRITE(p, " index2 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); - WRITE(p, " col = uvec4(t3.rgba * 15.99);\n"); - WRITE(p, " index3 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); - WRITE(p, " }\n"); - WRITE(p, " break;\n"); - WRITE(p, " case 3:\n"); // 8888 - WRITE(p, " col = uvec4(t.rgba * 255.99);\n"); - WRITE(p, " index0 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); - WRITE(p, " if (bilinear) {\n"); - WRITE(p, " col = uvec4(t1.rgba * 255.99);\n"); - WRITE(p, " index1 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); - WRITE(p, " col = uvec4(t2.rgba * 255.99);\n"); - WRITE(p, " index2 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); - WRITE(p, " col = uvec4(t3.rgba * 255.99);\n"); - WRITE(p, " index3 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); - WRITE(p, " }\n"); - WRITE(p, " break;\n"); - WRITE(p, " };\n"); + + switch (shaderDepalFmt) { + case GE_FORMAT_565: + WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index0 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index1 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index2 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index3 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " }\n"); + break; + case GE_FORMAT_5551: + WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index0 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index1 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index2 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index3 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n"); + WRITE(p, " }\n"); + break; + case GE_FORMAT_4444: + WRITE(p, " col = uvec4(t.rgba * 15.99);\n"); + WRITE(p, " index0 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgba * 15.99);\n"); + WRITE(p, " index1 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgba * 15.99);\n"); + WRITE(p, " index2 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgba * 15.99);\n"); + WRITE(p, " index3 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n"); + WRITE(p, " }\n"); + break; + case GE_FORMAT_8888: + WRITE(p, " col = uvec4(t.rgba * 255.99);\n"); + WRITE(p, " index0 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgba * 255.99);\n"); + WRITE(p, " index1 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgba * 255.99);\n"); + WRITE(p, " index2 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgba * 255.99);\n"); + WRITE(p, " index3 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n"); + WRITE(p, " }\n"); + break; + } WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n"); p.C(" t = ").LoadTexture2D("pal", "ivec2(index0, 0)", 0).C(";\n"); WRITE(p, " if (bilinear && !(index0 == index1 && index1 == index2 && index2 == index3)) {\n"); WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n"); - p.C(" t1 = ").LoadTexture2D("pal", "ivec2(index1, 0)", 0).C(";\n"); - p.C(" t2 = ").LoadTexture2D("pal", "ivec2(index2, 0)", 0).C(";\n"); - p.C(" t3 = ").LoadTexture2D("pal", "ivec2(index3, 0)", 0).C(";\n"); + p.C(" t1 = ").LoadTexture2D("pal", "ivec2(index1, 0)", 0).C(";\n"); + p.C(" t2 = ").LoadTexture2D("pal", "ivec2(index2, 0)", 0).C(";\n"); + p.C(" t3 = ").LoadTexture2D("pal", "ivec2(index3, 0)", 0).C(";\n"); WRITE(p, " t = mix(t, t1, fraction.x);\n"); WRITE(p, " t2 = mix(t2, t3, fraction.x);\n"); WRITE(p, " t = mix(t, t2, fraction.y);\n"); diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 18fe625189..2a9732a154 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -202,12 +202,6 @@ std::string FragmentShaderDesc(const FShaderID &id) { if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat "; if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA "; if (id.Bit(FS_BIT_DEPTH_TEST_NEVER)) desc << "DepthNever "; - switch ((ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2)) { - case ShaderDepalMode::OFF: break; - case ShaderDepalMode::NORMAL: desc << "Depal "; break; - case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break; - case ShaderDepalMode::CLUT8_8888: desc << "CLUT8From8888Depal"; break; - } if (id.Bit(FS_BIT_COLOR_WRITEMASK)) desc << "WriteMask "; if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) { desc << "TClamp"; @@ -275,6 +269,20 @@ std::string FragmentShaderDesc(const FShaderID &id) { if (id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH)) desc << "(fetch)"; if (id.Bit(FS_BIT_MINMAX_DISCARD)) desc << "FragMinMaxDiscard "; if (id.Bit(FS_BIT_DEPTH_CLAMP)) desc << "FragDepthClamp "; + + const ShaderDepalMode depalMode = (ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2); + switch (depalMode) { + case ShaderDepalMode::OFF: break; + case ShaderDepalMode::NORMAL: desc << "Depal("; + { + const GEBufferFormat shaderDepalFormat = (GEBufferFormat)id.Bits(FS_BIT_SHADER_DEPAL_FORMAT, 3); + desc << GeBufferFormatToString(shaderDepalFormat) << ") "; + break; + } + case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break; + case ShaderDepalMode::CLUT8_8888: desc << "CLUT8From8888Depal"; break; + } + return desc.str(); } @@ -324,6 +332,10 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip bool enableTexAlpha = gstate.isTextureAlphaUsed(); ShaderDepalMode shaderDepalMode = gstate_c.shaderDepalMode; + GEBufferFormat shaderDepalFormat = {}; + if (shaderDepalMode == ShaderDepalMode::NORMAL) { + shaderDepalFormat = gstate_c.depalTextureFormat; + } bool colorWriteMask = pipelineState.maskState.applyFramebufferRead; ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend; @@ -343,6 +355,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip } id.SetBit(FS_BIT_BGRA_TEXTURE, gstate_c.bgraTexture); id.SetBits(FS_BIT_SHADER_DEPAL_MODE, 2, (int)shaderDepalMode); + id.SetBits(FS_BIT_SHADER_DEPAL_FORMAT, 3, (int)shaderDepalFormat); id.SetBit(FS_BIT_3D_TEXTURE, gstate_c.curTextureIs3D); } diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 88c955bbf3..c5a97ccff0 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -88,7 +88,7 @@ enum FShaderBit : uint8_t { FS_BIT_SHADER_TEX_CLAMP = 5, FS_BIT_CLAMP_S = 6, FS_BIT_CLAMP_T = 7, - // Free bits: 8-11 + FS_BIT_SHADER_DEPAL_FORMAT = 8, // 3 bits (GEBufferFormat), connected to FS_BIT_SHADER_DEPAL_MODE FS_BIT_ALPHA_TEST = 12, FS_BIT_ALPHA_TEST_FUNC = 13, // 3 bits FS_BIT_ALPHA_AGAINST_ZERO = 16, diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 1736e36b66..5c75c79b9a 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -191,7 +191,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool useBuffe int indexMask = gstate.getClutIndexMask(); int indexShift = gstate.getClutIndexShift(); int indexOffset = gstate.getClutIndexStartPos() >> 4; - int format = gstate_c.depalFramebufferFormat; + int format = gstate_c.depalTextureFormat; uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format); // Poke in a bilinear filter flag in the top bit. if (gstate.isMagnifyFilteringEnabled()) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 001f318a62..41a9f5c3d2 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2319,9 +2319,9 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer mode = ShaderDepalMode::SMOOTHED; } - gstate_c.Dirty(DIRTY_DEPAL); + gstate_c.Dirty(DIRTY_DEPAL | DIRTY_FRAGMENTSHADER_STATE); gstate_c.SetUseShaderDepal(mode); - gstate_c.depalFramebufferFormat = framebuffer->fb_format; + gstate_c.depalTextureFormat = framebuffer->fb_format; const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index d1564e2ff5..74c3cc972b 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -377,7 +377,7 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc int indexMask = gstate.getClutIndexMask(); int indexShift = gstate.getClutIndexShift(); int indexOffset = gstate.getClutIndexStartPos() >> 4; - int format = gstate_c.depalFramebufferFormat; + int format = gstate_c.depalTextureFormat; uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format); // Poke in a bilinear filter flag in the top bit. val |= gstate.isMagnifyFilteringEnabled() << 31; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index ff45d0a721..151496fec6 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -700,7 +700,7 @@ public: int spline_num_points_u; ShaderDepalMode shaderDepalMode; - GEBufferFormat depalFramebufferFormat; + GEBufferFormat depalTextureFormat; u32 getRelativeAddress(u32 data) const { u32 baseExtended = ((gstate.base & 0x000F0000) << 8) | data;