Latte: Omit FragCoordScale uniform in shader if unused

We insert padding when needed to keep alignment intact for backwards compatibility with gfx pack shaders.
This commit is contained in:
Exzap 2026-06-16 00:13:21 +02:00
parent 5cf665f281
commit 0d9eda1654
5 changed files with 72 additions and 9 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}