Add shader non-uniform indexing support (#91)

This PR marks ALL texture indexes as nonuniform to fix an issue with the paths in Tomodachi Life: Living the Dream on AMD cards. It should have a negligible impact on performance (and it should not have an impact at all on NVIDIA cards!)

It's caused by what is called 'implicit non-uniform sampler array indexing'. The idea is basically that some GPUs optimize texture lookups from indexed texture arrays, by assuming that you are never going to index different textures within a single workgroup. What this causes is that visual glitch where a subgroup is tasked with rendering a block of the screen, and in the boundaries some cores are indexing the wrong texture.

Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/91
This commit is contained in:
AsperTheDog
2026-05-14 08:59:10 +00:00
committed by greem
parent dcac29680a
commit c0078088dd
11 changed files with 74 additions and 5 deletions

View File

@@ -587,6 +587,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return OperationResult.Invalid;
}
private static void MarkNonUniform(CodeGenContext context, SpvInstruction inst)
{
if (context.HostCapabilities.SupportsShaderNonUniformIndexing)
{
context.Decorate(inst, Decoration.NonUniform);
}
}
private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -613,6 +621,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(imagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -683,15 +692,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (isIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -740,15 +755,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (isIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -1878,35 +1899,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
{
SpvInstruction image = declaration.Image;
bool imageIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (imageIndexed)
{
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
if (texOp.IsSeparate)
{
image = context.Load(declaration.ImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
SpvInstruction sampler = samplerDeclaration.Image;
bool samplerIndexed = samplerDeclaration.IsIndexed;
if (samplerDeclaration.IsIndexed)
if (samplerIndexed)
{
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
MarkNonUniform(context, sampler);
}
sampler = context.Load(samplerDeclaration.ImageType, sampler);
if (samplerIndexed)
{
MarkNonUniform(context, sampler);
}
image = context.SampledImage(declaration.SampledImageType, image, sampler);
if (imageIndexed || samplerIndexed)
{
MarkNonUniform(context, image);
}
}
else
{
image = context.Load(declaration.SampledImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
}
return image;