diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp index 0e468f02..6982b408 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp @@ -562,7 +562,7 @@ namespace LatteDecompiler bool alphaTestEnable = decompilerContext->contextRegistersNew->SX_ALPHA_TEST_CONTROL.get_ALPHA_TEST_ENABLE(); if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel && alphaTestEnable != 0) decompilerContext->hasUniformVarBlock = true; // uf_alphaTestRef - if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel) + if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel && decompilerContext->analyzer.hasFragCoordAccess) decompilerContext->hasUniformVarBlock = true; // uf_fragCoordScale if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex && decompilerContext->analyzer.outputPointSize && decompilerContext->analyzer.writesPointSize == false) decompilerContext->hasUniformVarBlock = true; // uf_pointSize @@ -607,6 +607,11 @@ namespace LatteDecompiler decompilerContext->output->resourceMappingMTL.uniformVarsBufferBindingPoint = decompilerContext->currentBufferBindingPointMTL; decompilerContext->currentBufferBindingPointMTL++; } + else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel) // compatibility workaround to keep binding indices the same with existing gfx pack shader replacements, we now only emit uf_fragCoord when needed and it can lead to hasUniformVarBlock being false + { + decompilerContext->currentBindingPointVK++; + decompilerContext->currentBufferBindingPointMTL++; + } // assign binding points to uniform buffers if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK) { @@ -1006,6 +1011,23 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD shaderContext->analyzer.gprUseMask[i / 8] |= (1 << (i % 8)); } } + // check if shader accesses frag coord + if (shader->shaderType == LatteConst::ShaderType::Pixel) + { + LatteShaderPSInputTable* psInputTable = LatteSHRC_GetPSInputTable(); + for (sint32 i = 0; i < psInputTable->count; i++) + { + sint32 gprIndex = i; + if ((shaderContext->analyzer.gprUseMask[gprIndex / 8] & (1 << (gprIndex % 8))) == 0 && shaderContext->analyzer.usesRelativeGPRRead == false) + continue; + uint32 psInputSemanticId = psInputTable->import[i].semanticId; + if (psInputSemanticId == LATTE_ANALYZER_IMPORT_INDEX_SPIPOSITION) + { + shaderContext->analyzer.hasFragCoordAccess = true; + break; + } + } + } // analyze CF stack sint32 cfCurrentStackDepth = 0; sint32 cfCurrentMaxStackDepth = 0; diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp index 0c98418a..9d6a4385 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp @@ -83,9 +83,15 @@ namespace LatteDecompiler } } // define uf_fragCoordScale which holds the xy scale for render target resolution vs effective resolution + bool compatNeedFragCoordScalePadding = false; // 2026-06-15 - uf_fragCoordScale is only emitted when accessed now. To keep compatible with old shader replacements we insert padding if its not the last element if (shader->shaderType == LatteConst::ShaderType::Pixel) { - if (rendererType == RendererAPI::OpenGL) + if (!decompilerContext->analyzer.hasFragCoordAccess) + { + // omit uf_fragCoordScale + compatNeedFragCoordScalePadding = true; + } + else if (rendererType == RendererAPI::OpenGL) { uniformCurrentOffset = (uniformCurrentOffset + 7)&~7; shaderSrc->add("uniform vec2 uf_fragCoordScale;" _CRLF); @@ -106,6 +112,20 @@ namespace LatteDecompiler { if (decompilerContext->analyzer.texUnitUsesTexelCoordinates.test(t) == false) continue; + if (compatNeedFragCoordScalePadding) + { + if (rendererType == RendererAPI::OpenGL) + { + uniformCurrentOffset = (uniformCurrentOffset + 7)&~7; + shaderSrc->add("uniform vec2 uf_fragCoordScaleCompatPadding;" _CRLF); uniformCurrentOffset += 8; + } + else + { + uniformCurrentOffset = (uniformCurrentOffset + 15)&~15; + shaderSrc->add("uniform vec4 uf_fragCoordScaleCompatPadding;" _CRLF); uniformCurrentOffset += 16; + } + compatNeedFragCoordScalePadding = false; + } uniformCurrentOffset = (uniformCurrentOffset + 7) & ~7; shaderSrc->addFmt("uniform vec2 uf_tex{}Scale;" _CRLF, t); uniformOffsets.offset_texScale[t] = uniformCurrentOffset; @@ -116,6 +136,7 @@ namespace LatteDecompiler (shader->shaderType == LatteConst::ShaderType::Vertex && decompilerContext->options->usesGeometryShader == false) || (shader->shaderType == LatteConst::ShaderType::Geometry) ) { + // note - we dont need to handle compatNeedFragCoordScalePadding here because it's pixel shader only shaderSrc->add("uniform int uf_verticesPerInstance;" _CRLF); uniformOffsets.offset_verticesPerInstance = uniformCurrentOffset; uniformCurrentOffset += 4; @@ -280,7 +301,7 @@ namespace LatteDecompiler src->add("#define V2G_LAYOUT layout(location = 0)" _CRLF); } } - else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel) + else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel && decompilerContext->analyzer.hasFragCoordAccess) { src->add("#define GET_FRAGCOORD() vec4(gl_FragCoord.xy*uf_fragCoordScale.xy,gl_FragCoord.z, 1.0/gl_FragCoord.w)" _CRLF); } @@ -305,7 +326,7 @@ namespace LatteDecompiler if (decompilerContext->options->usesGeometryShader) src->add("#define V2G_LAYOUT" _CRLF); } - else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel) + else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel && decompilerContext->analyzer.hasFragCoordAccess) { src->add("#define GET_FRAGCOORD() vec4(gl_FragCoord.xy*uf_fragCoordScale,gl_FragCoord.zw)" _CRLF); } diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp index ac48f1f7..8ce05180 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp @@ -68,18 +68,34 @@ namespace LatteDecompiler } } // define fragCoordScale which holds the xy scale for render target resolution vs effective resolution + bool compatNeedFragCoordScalePadding = false; // 2026-06-15 - fragCoordScale is only emitted when accessed now. To keep compatible with old shader replacements we insert padding if its not the last element if (shader->shaderType == LatteConst::ShaderType::Pixel) { - uniformCurrentOffset = (uniformCurrentOffset + 7)&~7; - src->add("float2 fragCoordScale;" _CRLF); - uniformOffsets.offset_fragCoordScale = uniformCurrentOffset; - uniformCurrentOffset += 8; + if (!decompilerContext->analyzer.hasFragCoordAccess) + { + // omit fragCoordScale + compatNeedFragCoordScalePadding = true; + } + else + { + uniformCurrentOffset = (uniformCurrentOffset + 7)&~7; + src->add("float2 fragCoordScale;" _CRLF); + uniformOffsets.offset_fragCoordScale = uniformCurrentOffset; + uniformCurrentOffset += 8; + } } // provide scale factor for every texture that is accessed via texel coordinates (texelFetch) for (sint32 t = 0; t < LATTE_NUM_MAX_TEX_UNITS; t++) { if (decompilerContext->analyzer.texUnitUsesTexelCoordinates.test(t) == false) continue; + if (compatNeedFragCoordScalePadding) + { + uniformCurrentOffset = (uniformCurrentOffset + 7)&~7; + src->add("float2 fragCoordScaleCompatPadding;" _CRLF); + uniformCurrentOffset += 8; + compatNeedFragCoordScalePadding = false; + } uniformCurrentOffset = (uniformCurrentOffset + 7) & ~7; src->addFmt("float2 tex{}Scale;" _CRLF, t); uniformOffsets.offset_texScale[t] = uniformCurrentOffset; @@ -248,7 +264,8 @@ namespace LatteDecompiler { auto* src = shaderContext->shaderSource; - src->add("#define GET_FRAGCOORD() float4(in.position.xy * supportBuffer.fragCoordScale.xy, in.position.z, 1.0 / in.position.w)" _CRLF); + if (shaderContext->analyzer.hasFragCoordAccess) + src->add("#define GET_FRAGCOORD() float4(in.position.xy * supportBuffer.fragCoordScale.xy, in.position.z, 1.0 / in.position.w)" _CRLF); src->add("struct FragmentIn {" _CRLF); src->add("float4 position [[position]];" _CRLF); diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h index 0b19bcae..c9632044 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h @@ -237,6 +237,7 @@ struct LatteDecompilerShaderContext uint8 gprUseMask[(LATTE_NUM_GPR + 7) / 8]; // 1 bit per GPR, set if GPR is read/written anywhere in the program (ignores GPR accesses with relative index) bool hasStreamoutWrite; // stream-out CF instructions are used bool hasRedcCUBE; // has cube reduction instruction + bool hasFragCoordAccess{false}; // accesses gl_FragCoord bool modifiesPixelActiveState; // set if the active mask is changed anywhere in the shader (If false, we can skip active mask checks) bool usesIntegerValues; // set if the shader uses any kind of integer instruction or integer-based GPR/AR access sint32 activeStackMaxDepth; // maximum depth of pixel state stack diff --git a/src/Cafe/HW/Latte/Renderer/RendererShader.cpp b/src/Cafe/HW/Latte/Renderer/RendererShader.cpp index 23c8d0ea..9203b3be 100644 --- a/src/Cafe/HW/Latte/Renderer/RendererShader.cpp +++ b/src/Cafe/HW/Latte/Renderer/RendererShader.cpp @@ -19,6 +19,8 @@ uint32 RendererShader::GeneratePrecompiledCacheId() // settings that can influence shaders v += (uint32)g_current_game_profile->GetAccurateShaderMul() * 133; + v += 0x820a5277; // change this value for manual invalidation + return v; }