Stop identifying shader textures with handle and cbuf, use binding instead (#5266)

* Stop identifying shader textures with handle and cbuf, use binding instead

* Remove now unused code

* Consider image operations as having accurate type information too

I don't know why that was not the case before

* Fix missing unscale on InsertCoordNormalization, stop calling SetUsageFlagsForTextureQuery when not needed

* Shader cache version bump

* Change get texture methods to return descriptors created from ResourceManager state

 This is required to ensure that reserved textures and images will not be bound as a guest texture/image

* Fix BindlessElimination.SetHandle inserting coords at the wrong place
This commit is contained in:
gdkchan 2023-07-03 14:29:27 -03:00 committed by GitHub
parent 3b46bb73f7
commit 1c7a90ef35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 656 additions and 659 deletions

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 5311; private const uint CodeGenVersion = 5266;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View file

@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader
public readonly byte Slot; public readonly byte Slot;
public readonly byte SbCbSlot; public readonly byte SbCbSlot;
public readonly ushort SbCbOffset; public readonly ushort SbCbOffset;
public BufferUsageFlags Flags; public readonly BufferUsageFlags Flags;
public BufferDescriptor(int binding, int slot) public BufferDescriptor(int binding, int slot)
{ {
@ -16,25 +16,16 @@ namespace Ryujinx.Graphics.Shader
Slot = (byte)slot; Slot = (byte)slot;
SbCbSlot = 0; SbCbSlot = 0;
SbCbOffset = 0; SbCbOffset = 0;
Flags = BufferUsageFlags.None; Flags = BufferUsageFlags.None;
} }
public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset) public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset, BufferUsageFlags flags)
{ {
Binding = binding; Binding = binding;
Slot = (byte)slot; Slot = (byte)slot;
SbCbSlot = (byte)sbCbSlot; SbCbSlot = (byte)sbCbSlot;
SbCbOffset = (ushort)sbCbOffset; SbCbOffset = (ushort)sbCbOffset;
Flags = flags;
Flags = BufferUsageFlags.None;
}
public BufferDescriptor SetFlag(BufferUsageFlags flag)
{
Flags |= flag;
return this;
} }
} }
} }

View file

@ -75,22 +75,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false); DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false);
DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true); DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true);
DeclareSamplers(context, context.Config.Properties.Textures.Values);
var textureDescriptors = context.Config.GetTextureDescriptors(); DeclareImages(context, context.Config.Properties.Images.Values);
if (textureDescriptors.Length != 0)
{
DeclareSamplers(context, textureDescriptors);
context.AppendLine();
}
var imageDescriptors = context.Config.GetImageDescriptors();
if (imageDescriptors.Length != 0)
{
DeclareImages(context, imageDescriptors);
context.AppendLine();
}
if (context.Config.Stage != ShaderStage.Compute) if (context.Config.Stage != ShaderStage.Compute)
{ {
@ -369,80 +355,71 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
} }
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> definitions)
{ {
int arraySize = 0; int arraySize = 0;
foreach (var descriptor in descriptors)
foreach (var definition in definitions)
{ {
if (descriptor.Type.HasFlag(SamplerType.Indexed)) string indexExpr = string.Empty;
if (definition.Type.HasFlag(SamplerType.Indexed))
{ {
if (arraySize == 0) if (arraySize == 0)
{ {
arraySize = ShaderConfig.SamplerArraySize; arraySize = ResourceManager.SamplerArraySize;
} }
else if (--arraySize != 0) else if (--arraySize != 0)
{ {
continue; continue;
} }
indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]";
} }
string indexExpr = NumberFormatter.FormatInt(arraySize); string samplerTypeName = definition.Type.ToGlslSamplerType();
string samplerName = OperandManager.GetSamplerName(
context.Config.Stage,
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Type.HasFlag(SamplerType.Indexed),
indexExpr);
string samplerTypeName = descriptor.Type.ToGlslSamplerType();
string layout = string.Empty; string layout = string.Empty;
if (context.Config.Options.TargetApi == TargetApi.Vulkan) if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{ {
layout = ", set = 2"; layout = $", set = {definition.Set}";
} }
context.AppendLine($"layout (binding = {descriptor.Binding}{layout}) uniform {samplerTypeName} {samplerName};"); context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {samplerTypeName} {definition.Name}{indexExpr};");
} }
} }
private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors) private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> definitions)
{ {
int arraySize = 0; int arraySize = 0;
foreach (var descriptor in descriptors)
foreach (var definition in definitions)
{ {
if (descriptor.Type.HasFlag(SamplerType.Indexed)) string indexExpr = string.Empty;
if (definition.Type.HasFlag(SamplerType.Indexed))
{ {
if (arraySize == 0) if (arraySize == 0)
{ {
arraySize = ShaderConfig.SamplerArraySize; arraySize = ResourceManager.SamplerArraySize;
} }
else if (--arraySize != 0) else if (--arraySize != 0)
{ {
continue; continue;
} }
indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]";
} }
string indexExpr = NumberFormatter.FormatInt(arraySize); string imageTypeName = definition.Type.ToGlslImageType(definition.Format.GetComponentType());
string imageName = OperandManager.GetImageName( if (definition.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
context.Config.Stage,
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Format,
descriptor.Type.HasFlag(SamplerType.Indexed),
indexExpr);
string imageTypeName = descriptor.Type.ToGlslImageType(descriptor.Format.GetComponentType());
if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
{ {
imageTypeName = "coherent " + imageTypeName; imageTypeName = "coherent " + imageTypeName;
} }
string layout = descriptor.Format.ToGlslFormat(); string layout = definition.Format.ToGlslFormat();
if (!string.IsNullOrEmpty(layout)) if (!string.IsNullOrEmpty(layout))
{ {
@ -451,10 +428,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.Options.TargetApi == TargetApi.Vulkan) if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{ {
layout = $", set = 3{layout}"; layout = $", set = {definition.Set}{layout}";
} }
context.AppendLine($"layout (binding = {descriptor.Binding}{layout}) uniform {imageTypeName} {imageName};"); context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {imageTypeName} {definition.Name}{indexExpr};");
} }
} }

View file

@ -4,9 +4,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
public const string LocalNamePrefix = "temp"; public const string LocalNamePrefix = "temp";
public const string SamplerNamePrefix = "tex";
public const string ImageNamePrefix = "img";
public const string PerPatchAttributePrefix = "patch_attr_"; public const string PerPatchAttributePrefix = "patch_attr_";
public const string IAttributePrefix = "in_attr"; public const string IAttributePrefix = "in_attr";
public const string OAttributePrefix = "out_attr"; public const string OAttributePrefix = "out_attr";

View file

@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
indexExpr = Src(AggregateType.S32); indexExpr = Src(AggregateType.S32);
} }
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); string imageName = GetImageName(context.Config, texOp, indexExpr);
texCallBuilder.Append('('); texCallBuilder.Append('(');
texCallBuilder.Append(imageName); texCallBuilder.Append(imageName);
@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
} }
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); string samplerName = GetSamplerName(context.Config, texOp, indexExpr);
int coordsIndex = isBindless || isIndexed ? 1 : 0; int coordsIndex = isBindless || isIndexed ? 1 : 0;
@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
indexExpr = Src(AggregateType.S32); indexExpr = Src(AggregateType.S32);
} }
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); string samplerName = GetSamplerName(context.Config, texOp, indexExpr);
texCall += "(" + samplerName; texCall += "(" + samplerName;
@ -538,7 +538,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32); indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
} }
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); string samplerName = GetSamplerName(context.Config, texOp, indexExpr);
if (texOp.Index == 3) if (texOp.Index == 3)
{ {
@ -546,8 +546,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
} }
else else
{ {
TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp); context.Config.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition);
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer; bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer;
string texCall; string texCall;
if (hasLod) if (hasLod)
@ -715,6 +715,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return varName; return varName;
} }
private static string GetSamplerName(ShaderConfig config, AstTextureOperation texOp, string indexExpr)
{
string name = config.Properties.Textures[texOp.Binding].Name;
if (texOp.Type.HasFlag(SamplerType.Indexed))
{
name = $"{name}[{indexExpr}]";
}
return name;
}
private static string GetImageName(ShaderConfig config, AstTextureOperation texOp, string indexExpr)
{
string name = config.Properties.Images[texOp.Binding].Name;
if (texOp.Type.HasFlag(SamplerType.Indexed))
{
name = $"{name}[{indexExpr}]";
}
return name;
}
private static string GetMask(int index) private static string GetMask(int index)
{ {
return $".{"rgba".AsSpan(index, 1)}"; return $".{"rgba".AsSpan(index, 1)}";

View file

@ -11,9 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
class OperandManager class OperandManager
{ {
private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private Dictionary<AstOperand, string> _locals;
private readonly Dictionary<AstOperand, string> _locals;
public OperandManager() public OperandManager()
{ {
@ -41,60 +39,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}; };
} }
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{
return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
}
public static string GetSamplerName(ShaderStage stage, int cbufSlot, int handle, bool indexed, string indexExpr)
{
string suffix = cbufSlot < 0 ? $"_tcb_{handle:X}" : $"_cb{cbufSlot}_{handle:X}";
if (indexed)
{
suffix += $"a[{indexExpr}]";
}
return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix;
}
public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{
return GetImageName(stage, texOp.CbufSlot, texOp.Handle, texOp.Format, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
}
public static string GetImageName(
ShaderStage stage,
int cbufSlot,
int handle,
TextureFormat format,
bool indexed,
string indexExpr)
{
string suffix = cbufSlot < 0
? $"_tcb_{handle:X}_{format.ToGlslFormat()}"
: $"_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
if (indexed)
{
suffix += $"a[{indexExpr}]";
}
return GetShaderStagePrefix(stage) + "_" + DefaultNames.ImageNamePrefix + suffix;
}
public static string GetShaderStagePrefix(ShaderStage stage)
{
int index = (int)stage;
if ((uint)index >= _stagePrefixes.Length)
{
return "invalid";
}
return _stagePrefixes[index];
}
public static string GetArgumentName(int argIndex) public static string GetArgumentName(int argIndex)
{ {
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; return $"{DefaultNames.ArgumentNamePrefix}{argIndex}";

View file

@ -28,9 +28,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>(); public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>();
public Dictionary<int, Instruction> LocalMemories { get; } = new Dictionary<int, Instruction>(); public Dictionary<int, Instruction> LocalMemories { get; } = new Dictionary<int, Instruction>();
public Dictionary<int, Instruction> SharedMemories { get; } = new Dictionary<int, Instruction>(); public Dictionary<int, Instruction> SharedMemories { get; } = new Dictionary<int, Instruction>();
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>(); public Dictionary<int, SamplerType> SamplersTypes { get; } = new Dictionary<int, SamplerType>();
public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>(); public Dictionary<int, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<int, (Instruction, Instruction, Instruction)>();
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>(); public Dictionary<int, (Instruction, Instruction)> Images { get; } = new Dictionary<int, (Instruction, Instruction)>();
public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>(); public Dictionary<IoDefinition, Instruction> Inputs { get; } = new Dictionary<IoDefinition, Instruction>();
public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>(); public Dictionary<IoDefinition, Instruction> Outputs { get; } = new Dictionary<IoDefinition, Instruction>();
public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>(); public Dictionary<IoDefinition, Instruction> InputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();

View file

@ -72,8 +72,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private); DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private);
DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup); DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup);
DeclareSamplers(context, context.Config.GetTextureDescriptors()); DeclareSamplers(context, context.Config.Properties.Textures.Values);
DeclareImages(context, context.Config.GetImageDescriptors()); DeclareImages(context, context.Config.Properties.Images.Values);
DeclareInputsAndOutputs(context, info); DeclareInputsAndOutputs(context, info);
} }
@ -110,6 +110,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
foreach (BufferDefinition buffer in buffers) foreach (BufferDefinition buffer in buffers)
{ {
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? buffer.Set : 0;
int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4; int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4;
int alignmentMask = alignment - 1; int alignmentMask = alignment - 1;
int offset = 0; int offset = 0;
@ -163,7 +164,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var variable = context.Variable(pointerType, StorageClass.Uniform); var variable = context.Variable(pointerType, StorageClass.Uniform);
context.Name(variable, buffer.Name); context.Name(variable, buffer.Name);
context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set); context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding); context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding);
context.AddGlobalVariable(variable); context.AddGlobalVariable(variable);
@ -178,92 +179,72 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
} }
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> samplers)
{ {
foreach (var descriptor in descriptors) foreach (var sampler in samplers)
{ {
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format); int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? sampler.Set : 0;
if (context.Samplers.ContainsKey(meta)) var dim = (sampler.Type & SamplerType.Mask) switch
{
continue;
}
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0;
var dim = (descriptor.Type & SamplerType.Mask) switch
{ {
SamplerType.Texture1D => Dim.Dim1D, SamplerType.Texture1D => Dim.Dim1D,
SamplerType.Texture2D => Dim.Dim2D, SamplerType.Texture2D => Dim.Dim2D,
SamplerType.Texture3D => Dim.Dim3D, SamplerType.Texture3D => Dim.Dim3D,
SamplerType.TextureCube => Dim.Cube, SamplerType.TextureCube => Dim.Cube,
SamplerType.TextureBuffer => Dim.Buffer, SamplerType.TextureBuffer => Dim.Buffer,
_ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\"."), _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\".")
}; };
var imageType = context.TypeImage( var imageType = context.TypeImage(
context.TypeFP32(), context.TypeFP32(),
dim, dim,
descriptor.Type.HasFlag(SamplerType.Shadow), sampler.Type.HasFlag(SamplerType.Shadow),
descriptor.Type.HasFlag(SamplerType.Array), sampler.Type.HasFlag(SamplerType.Array),
descriptor.Type.HasFlag(SamplerType.Multisample), sampler.Type.HasFlag(SamplerType.Multisample),
1, 1,
ImageFormat.Unknown); ImageFormat.Unknown);
var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}";
var sampledImageType = context.TypeSampledImage(imageType); var sampledImageType = context.TypeSampledImage(imageType);
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType); var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant); var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable)); context.Samplers.Add(sampler.Binding, (imageType, sampledImageType, sampledImageVariable));
context.SamplersTypes.Add(meta, descriptor.Type); context.SamplersTypes.Add(sampler.Binding, sampler.Type);
context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}"); context.Name(sampledImageVariable, sampler.Name);
context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding); context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)sampler.Binding);
context.AddGlobalVariable(sampledImageVariable); context.AddGlobalVariable(sampledImageVariable);
} }
} }
private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors) private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> images)
{ {
foreach (var descriptor in descriptors) foreach (var image in images)
{ {
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format); int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? image.Set : 0;
if (context.Images.ContainsKey(meta)) var dim = GetDim(image.Type);
{
continue;
}
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0;
var dim = GetDim(descriptor.Type);
var imageType = context.TypeImage( var imageType = context.TypeImage(
context.GetType(meta.Format.GetComponentType()), context.GetType(image.Format.GetComponentType()),
dim, dim,
descriptor.Type.HasFlag(SamplerType.Shadow), image.Type.HasFlag(SamplerType.Shadow),
descriptor.Type.HasFlag(SamplerType.Array), image.Type.HasFlag(SamplerType.Array),
descriptor.Type.HasFlag(SamplerType.Multisample), image.Type.HasFlag(SamplerType.Multisample),
AccessQualifier.ReadWrite, AccessQualifier.ReadWrite,
GetImageFormat(meta.Format)); GetImageFormat(image.Format));
var nameSuffix = meta.CbufSlot < 0 ?
$"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" :
$"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}";
var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType); var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant); var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
context.Images.Add(meta, (imageType, imageVariable)); context.Images.Add(image.Binding, (imageType, imageVariable));
context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}"); context.Name(imageVariable, image.Name);
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex); context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding); context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)image.Binding);
if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) if (image.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
{ {
context.Decorate(imageVariable, Decoration.Coherent); context.Decorate(imageVariable, Decoration.Coherent);
} }

View file

@ -657,7 +657,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction value = Src(componentType); SpvInstruction value = Src(componentType);
(SpvInstruction imageType, SpvInstruction imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; (var imageType, var imageVariable) = context.Images[texOp.Binding];
context.Load(imageType, imageVariable); context.Load(imageType, imageVariable);
@ -742,7 +742,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.S32); pCoords = Src(AggregateType.S32);
} }
var (imageType, imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; (var imageType, var imageVariable) = context.Images[texOp.Binding];
var image = context.Load(imageType, imageVariable); var image = context.Load(imageType, imageVariable);
var imageComponentType = context.GetType(componentType); var imageComponentType = context.GetType(componentType);
@ -829,7 +829,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems); var texel = context.CompositeConstruct(context.TypeVector(context.GetType(componentType), ComponentsCount), cElems);
var (imageType, imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)]; (var imageType, var imageVariable) = context.Images[texOp.Binding];
var image = context.Load(imageType, imageVariable); var image = context.Load(imageType, imageVariable);
@ -908,9 +908,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
pCoords = Src(AggregateType.FP32); pCoords = Src(AggregateType.FP32);
} }
var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); (_, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
var (_, sampledImageType, sampledImageVariable) = context.Samplers[meta];
var image = context.Load(sampledImageType, sampledImageVariable); var image = context.Load(sampledImageType, sampledImageVariable);
@ -1511,9 +1509,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32(); var resultType = colorIsVector ? context.TypeVector(context.TypeFP32(), 4) : context.TypeFP32();
var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
var (imageType, sampledImageType, sampledImageVariable) = context.Samplers[meta];
var image = context.Load(sampledImageType, sampledImageVariable); var image = context.Load(sampledImageType, sampledImageVariable);
@ -1592,9 +1588,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.GetS32(texOp.GetSource(0)); context.GetS32(texOp.GetSource(0));
} }
var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
(SpvInstruction imageType, SpvInstruction sampledImageType, SpvInstruction sampledImageVariable) = context.Samplers[meta];
var image = context.Load(sampledImageType, sampledImageVariable); var image = context.Load(sampledImageType, sampledImageVariable);
image = context.Image(imageType, image); image = context.Image(imageType, image);
@ -1605,7 +1599,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
else else
{ {
var type = context.SamplersTypes[meta]; var type = context.SamplersTypes[texOp.Binding];
bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer; bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer;
int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions(); int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions();

View file

@ -1,4 +0,0 @@
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
readonly record struct TextureMeta(int CbufSlot, int Handle, TextureFormat Format);
}

View file

@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return context.Copy(Register(srcB++, RegisterType.Gpr)); return context.Copy(Register(srcB++, RegisterType.Gpr));
} }
Operand destOperand = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null; Operand d = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null;
List<Operand> sourcesList = new(); List<Operand> sourcesList = new();
@ -277,17 +277,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Bindless; flags |= TextureFlags.Bindless;
} }
TextureOperation operation = context.CreateTextureOperation( int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageAtomic, Instruction.ImageAtomic,
type, type,
format, format,
flags, flags,
imm, TextureOperation.DefaultCbufSlot,
0, imm);
new[] { destOperand },
sources);
context.Add(operation); Operand res = context.ImageAtomic(type, format, flags, binding, sources);
context.Copy(d, res);
} }
private static void EmitSuld( private static void EmitSuld(
@ -383,21 +383,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
Array.Resize(ref dests, outputIndex); Array.Resize(ref dests, outputIndex);
} }
TextureOperation operation = context.CreateTextureOperation( TextureFormat format = isBindless ? TextureFormat.Unknown : context.Config.GetTextureFormat(handle);
int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageLoad, Instruction.ImageLoad,
type, type,
format,
flags, flags,
handle, TextureOperation.DefaultCbufSlot,
(int)componentMask, handle);
dests,
sources);
if (!isBindless) context.ImageLoad(type, format, flags, binding, (int)componentMask, dests, sources);
{
operation.Format = context.Config.GetTextureFormat(handle);
}
context.Add(operation);
} }
else else
{ {
@ -430,17 +426,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
Array.Resize(ref dests, outputIndex); Array.Resize(ref dests, outputIndex);
} }
TextureOperation operation = context.CreateTextureOperation( TextureFormat format = GetTextureFormat(size);
int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageLoad, Instruction.ImageLoad,
type, type,
GetTextureFormat(size), format,
flags, flags,
handle, TextureOperation.DefaultCbufSlot,
compMask, handle);
dests,
sources);
context.Add(operation); context.ImageLoad(type, format, flags, binding, compMask, dests, sources);
switch (size) switch (size)
{ {
@ -552,17 +548,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Bindless; flags |= TextureFlags.Bindless;
} }
TextureOperation operation = context.CreateTextureOperation( int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageAtomic, Instruction.ImageAtomic,
type, type,
format, format,
flags, flags,
imm, TextureOperation.DefaultCbufSlot,
0, imm);
null,
sources);
context.Add(operation); context.ImageAtomic(type, format, flags, binding, sources);
} }
private static void EmitSust( private static void EmitSust(
@ -681,17 +675,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Coherent; flags |= TextureFlags.Coherent;
} }
TextureOperation operation = context.CreateTextureOperation( int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.ImageStore, Instruction.ImageStore,
type, type,
format, format,
flags, flags,
handle, TextureOperation.DefaultCbufSlot,
0, handle);
null,
sources);
context.Add(operation); context.ImageStore(type, format, flags, binding, sources);
} }
private static int GetComponentSizeInBytesLog2(SuatomSize size) private static int GetComponentSizeInBytesLog2(SuatomSize size)

View file

@ -324,16 +324,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
int handle = !isBindless ? imm : 0; int handle = !isBindless ? imm : 0;
TextureOperation operation = context.CreateTextureOperation( EmitTextureSample(context, type, flags, handle, componentMask, dests, sources);
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
context.Add(operation);
} }
private static void EmitTexs( private static void EmitTexs(
@ -657,16 +648,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Array.Resize(ref dests, outputIndex); Array.Resize(ref dests, outputIndex);
} }
TextureOperation operation = context.CreateTextureOperation( EmitTextureSample(context, type, flags, handle, componentMask, dests, sources);
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
context.Add(operation);
if (isF16) if (isF16)
{ {
@ -812,18 +794,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Array.Resize(ref dests, outputIndex); Array.Resize(ref dests, outputIndex);
} }
int handle = imm; EmitTextureSample(context, type, flags, imm, componentMask, dests, sources);
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
context.Add(operation);
} }
private static void EmitTmml( private static void EmitTmml(
@ -913,15 +884,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Register(dest++, RegisterType.Gpr); return Register(dest++, RegisterType.Gpr);
} }
int handle = imm; int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.Lod,
type,
TextureFormat.Unknown,
flags,
TextureOperation.DefaultCbufSlot,
imm);
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{ {
if ((compMask & 1) != 0) if ((compMask & 1) != 0)
{ {
Operand destOperand = GetDest(); Operand d = GetDest();
if (destOperand == null) if (d == null)
{ {
break; break;
} }
@ -930,28 +907,18 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (compIndex >= 2) if (compIndex >= 2)
{ {
context.Add(new CommentNode("Unsupported component z or w found")); context.Add(new CommentNode("Unsupported component z or w found"));
context.Copy(destOperand, Const(0)); context.Copy(d, Const(0));
} }
else else
{ {
Operand tempDest = Local(); // The instruction component order is the inverse of GLSL's.
Operand res = context.Lod(type, flags, binding, compIndex ^ 1, sources);
TextureOperation operation = context.CreateTextureOperation( res = context.FPMultiply(res, ConstF(256.0f));
Instruction.Lod,
type,
flags,
handle,
compIndex ^ 1, // The instruction component order is the inverse of GLSL's.
new[] { tempDest },
sources);
context.Add(operation); Operand fixedPointValue = context.FP32ConvertToS32(res);
tempDest = context.FPMultiply(tempDest, ConstF(256.0f)); context.Copy(d, fixedPointValue);
Operand fixedPointValue = context.FP32ConvertToS32(tempDest);
context.Copy(destOperand, fixedPointValue);
} }
} }
} }
@ -1081,18 +1048,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Array.Resize(ref dests, outputIndex); Array.Resize(ref dests, outputIndex);
} }
int handle = imm; EmitTextureSample(context, type, flags, imm, componentMask, dests, sources);
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
componentMask,
dests,
sources);
context.Add(operation);
} }
private static void EmitTxq( private static void EmitTxq(
@ -1111,10 +1067,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
// TODO: Validate and use query.
Instruction inst = Instruction.TextureSize;
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
Operand Ra() Operand Ra()
{ {
if (srcA > RegisterConsts.RegisterZeroIndex) if (srcA > RegisterConsts.RegisterZeroIndex)
@ -1157,31 +1109,55 @@ namespace Ryujinx.Graphics.Shader.Instructions
type = context.Config.GpuAccessor.QuerySamplerType(imm); type = context.Config.GpuAccessor.QuerySamplerType(imm);
} }
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.TextureSize,
type,
TextureFormat.Unknown,
flags,
TextureOperation.DefaultCbufSlot,
imm);
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{ {
if ((compMask & 1) != 0) if ((compMask & 1) != 0)
{ {
Operand destOperand = GetDest(); Operand d = GetDest();
if (destOperand == null) if (d == null)
{ {
break; break;
} }
TextureOperation operation = context.CreateTextureOperation( // TODO: Validate and use query parameter.
inst, Operand res = context.TextureSize(type, flags, binding, compIndex, sources);
type,
flags,
imm,
compIndex,
new[] { destOperand },
sources);
context.Add(operation); context.Copy(d, res);
} }
} }
} }
private static void EmitTextureSample(
EmitterContext context,
SamplerType type,
TextureFlags flags,
int handle,
int componentMask,
Operand[] dests,
Operand[] sources)
{
int binding = flags.HasFlag(TextureFlags.Bindless) ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding(
Instruction.TextureSample,
type,
TextureFormat.Unknown,
flags,
TextureOperation.DefaultCbufSlot,
handle);
context.TextureSample(type, flags, binding, componentMask, dests, sources);
}
private static SamplerType ConvertSamplerType(TexDim dimensions) private static SamplerType ConvertSamplerType(TexDim dimensions)
{ {
return dimensions switch return dimensions switch

View file

@ -8,16 +8,14 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
public TextureFormat Format { get; set; } public TextureFormat Format { get; set; }
public TextureFlags Flags { get; private set; } public TextureFlags Flags { get; private set; }
public int CbufSlot { get; private set; } public int Binding { get; private set; }
public int Handle { get; private set; }
public TextureOperation( public TextureOperation(
Instruction inst, Instruction inst,
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int cbufSlot, int binding,
int handle,
int compIndex, int compIndex,
Operand[] dests, Operand[] dests,
Operand[] sources) : base(inst, compIndex, dests, sources) Operand[] sources) : base(inst, compIndex, dests, sources)
@ -25,30 +23,17 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Type = type; Type = type;
Format = format; Format = format;
Flags = flags; Flags = flags;
CbufSlot = cbufSlot; Binding = binding;
Handle = handle;
} }
public TextureOperation( public void TurnIntoIndexed(int binding)
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int handle,
int compIndex,
Operand[] dests,
Operand[] sources) : this(inst, type, format, flags, DefaultCbufSlot, handle, compIndex, dests, sources)
{
}
public void TurnIntoIndexed(int handle)
{ {
Type |= SamplerType.Indexed; Type |= SamplerType.Indexed;
Flags &= ~TextureFlags.Bindless; Flags &= ~TextureFlags.Bindless;
Handle = handle; Binding = binding;
} }
public void SetHandle(int handle, int cbufSlot = DefaultCbufSlot) public void SetBinding(int binding)
{ {
if ((Flags & TextureFlags.Bindless) != 0) if ((Flags & TextureFlags.Bindless) != 0)
{ {
@ -57,8 +42,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
RemoveSource(0); RemoveSource(0);
} }
CbufSlot = cbufSlot; Binding = binding;
Handle = handle;
} }
public void SetLodLevelFlag() public void SetLodLevelFlag()

View file

@ -8,24 +8,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public TextureFormat Format { get; } public TextureFormat Format { get; }
public TextureFlags Flags { get; } public TextureFlags Flags { get; }
public int CbufSlot { get; } public int Binding { get; }
public int Handle { get; }
public AstTextureOperation( public AstTextureOperation(
Instruction inst, Instruction inst,
SamplerType type, SamplerType type,
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int cbufSlot, int binding,
int handle,
int index, int index,
params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length)
{ {
Type = type; Type = type;
Format = format; Format = format;
Flags = flags; Flags = flags;
CbufSlot = cbufSlot; Binding = binding;
Handle = handle;
} }
} }
} }

View file

@ -6,11 +6,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
private readonly Dictionary<int, BufferDefinition> _constantBuffers; private readonly Dictionary<int, BufferDefinition> _constantBuffers;
private readonly Dictionary<int, BufferDefinition> _storageBuffers; private readonly Dictionary<int, BufferDefinition> _storageBuffers;
private readonly Dictionary<int, TextureDefinition> _textures;
private readonly Dictionary<int, TextureDefinition> _images;
private readonly Dictionary<int, MemoryDefinition> _localMemories; private readonly Dictionary<int, MemoryDefinition> _localMemories;
private readonly Dictionary<int, MemoryDefinition> _sharedMemories; private readonly Dictionary<int, MemoryDefinition> _sharedMemories;
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers; public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers; public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
public IReadOnlyDictionary<int, TextureDefinition> Textures => _textures;
public IReadOnlyDictionary<int, TextureDefinition> Images => _images;
public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories; public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories;
public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories; public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories;
@ -18,20 +22,32 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
_constantBuffers = new Dictionary<int, BufferDefinition>(); _constantBuffers = new Dictionary<int, BufferDefinition>();
_storageBuffers = new Dictionary<int, BufferDefinition>(); _storageBuffers = new Dictionary<int, BufferDefinition>();
_textures = new Dictionary<int, TextureDefinition>();
_images = new Dictionary<int, TextureDefinition>();
_localMemories = new Dictionary<int, MemoryDefinition>(); _localMemories = new Dictionary<int, MemoryDefinition>();
_sharedMemories = new Dictionary<int, MemoryDefinition>(); _sharedMemories = new Dictionary<int, MemoryDefinition>();
} }
public void AddConstantBuffer(int binding, BufferDefinition definition) public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition)
{ {
_constantBuffers[binding] = definition; _constantBuffers[binding] = definition;
} }
public void AddStorageBuffer(int binding, BufferDefinition definition) public void AddOrUpdateStorageBuffer(int binding, BufferDefinition definition)
{ {
_storageBuffers[binding] = definition; _storageBuffers[binding] = definition;
} }
public void AddOrUpdateTexture(int binding, TextureDefinition descriptor)
{
_textures[binding] = descriptor;
}
public void AddOrUpdateImage(int binding, TextureDefinition descriptor)
{
_images[binding] = descriptor;
}
public int AddLocalMemory(MemoryDefinition definition) public int AddLocalMemory(MemoryDefinition definition)
{ {
int id = _localMemories.Count; int id = _localMemories.Count;

View file

@ -125,15 +125,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
AstTextureOperation GetAstTextureOperation(TextureOperation texOp) AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
{ {
return new AstTextureOperation( return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.Index, sources);
inst,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
texOp.Index,
sources);
} }
int componentsCount = BitOperations.PopCount((uint)operation.Index); int componentsCount = BitOperations.PopCount((uint)operation.Index);

View file

@ -0,0 +1,27 @@
namespace Ryujinx.Graphics.Shader
{
readonly struct TextureDefinition
{
public int Set { get; }
public int Binding { get; }
public string Name { get; }
public SamplerType Type { get; }
public TextureFormat Format { get; }
public TextureUsageFlags Flags { get; }
public TextureDefinition(int set, int binding, string name, SamplerType type, TextureFormat format, TextureUsageFlags flags)
{
Set = set;
Binding = binding;
Name = name;
Type = type;
Format = format;
Flags = flags;
}
public TextureDefinition SetFlag(TextureUsageFlags flag)
{
return new TextureDefinition(Set, Binding, Name, Type, Format, Flags | flag);
}
}
}

View file

@ -12,23 +12,16 @@ namespace Ryujinx.Graphics.Shader
public readonly int CbufSlot; public readonly int CbufSlot;
public readonly int HandleIndex; public readonly int HandleIndex;
public TextureUsageFlags Flags; public readonly TextureUsageFlags Flags;
public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex) public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int cbufSlot, int handleIndex, TextureUsageFlags flags)
{ {
Binding = binding; Binding = binding;
Type = type; Type = type;
Format = format; Format = format;
CbufSlot = cbufSlot; CbufSlot = cbufSlot;
HandleIndex = handleIndex; HandleIndex = handleIndex;
Flags = TextureUsageFlags.None; Flags = flags;
}
public TextureDescriptor SetFlag(TextureUsageFlags flag)
{
Flags |= flag;
return this;
} }
} }
} }

View file

@ -115,36 +115,6 @@ namespace Ryujinx.Graphics.Shader.Translation
_operations.Add(operation); _operations.Add(operation);
} }
public TextureOperation CreateTextureOperation(
Instruction inst,
SamplerType type,
TextureFlags flags,
int handle,
int compIndex,
Operand[] dests,
params Operand[] sources)
{
return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dests, sources);
}
public TextureOperation CreateTextureOperation(
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int handle,
int compIndex,
Operand[] dests,
params Operand[] sources)
{
if (!flags.HasFlag(TextureFlags.Bindless))
{
Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle);
}
return new TextureOperation(inst, type, format, flags, handle, compIndex, dests, sources);
}
public void FlagAttributeRead(int attribute) public void FlagAttributeRead(int attribute)
{ {
if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId)

View file

@ -604,6 +604,45 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.Subtract, Local(), a, b); return context.Add(Instruction.Subtract, Local(), a, b);
} }
public static Operand ImageAtomic(
this EmitterContext context,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int binding,
Operand[] sources)
{
Operand dest = Local();
context.Add(new TextureOperation(Instruction.ImageAtomic, type, format, flags, binding, 0, new[] { dest }, sources));
return dest;
}
public static void ImageLoad(
this EmitterContext context,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int binding,
int compMask,
Operand[] dests,
Operand[] sources)
{
context.Add(new TextureOperation(Instruction.ImageLoad, type, format, flags, binding, compMask, dests, sources));
}
public static void ImageStore(
this EmitterContext context,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int binding,
Operand[] sources)
{
context.Add(new TextureOperation(Instruction.ImageStore, type, format, flags, binding, 0, null, sources));
}
public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32) public static Operand IsNan(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32)
{ {
return context.Add(fpType | Instruction.IsNan, Local(), a); return context.Add(fpType | Instruction.IsNan, Local(), a);
@ -666,6 +705,21 @@ namespace Ryujinx.Graphics.Shader.Translation
: context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex); : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex);
} }
public static Operand Lod(
this EmitterContext context,
SamplerType type,
TextureFlags flags,
int binding,
int compIndex,
Operand[] sources)
{
Operand dest = Local();
context.Add(new TextureOperation(Instruction.Lod, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
return dest;
}
public static Operand MemoryBarrier(this EmitterContext context) public static Operand MemoryBarrier(this EmitterContext context)
{ {
return context.Add(Instruction.MemoryBarrier); return context.Add(Instruction.MemoryBarrier);
@ -797,6 +851,33 @@ namespace Ryujinx.Graphics.Shader.Translation
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value); : context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
} }
public static void TextureSample(
this EmitterContext context,
SamplerType type,
TextureFlags flags,
int binding,
int compMask,
Operand[] dests,
Operand[] sources)
{
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
}
public static Operand TextureSize(
this EmitterContext context,
SamplerType type,
TextureFlags flags,
int binding,
int compIndex,
Operand[] sources)
{
Operand dest = Local();
context.Add(new TextureOperation(Instruction.TextureSize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
return dest;
}
public static Operand UnpackDouble2x32High(this EmitterContext context, Operand a) public static Operand UnpackDouble2x32High(this EmitterContext context, Operand a)
{ {
return UnpackDouble2x32(context, a, 1); return UnpackDouble2x32(context, a, 1);

View file

@ -222,8 +222,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot, bool rewriteSamplerType, bool isImage) private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot, bool rewriteSamplerType, bool isImage)
{ {
texOp.SetHandle(cbufOffset, cbufSlot);
if (rewriteSamplerType) if (rewriteSamplerType)
{ {
SamplerType newType = config.GpuAccessor.QuerySamplerType(cbufOffset, cbufSlot); SamplerType newType = config.GpuAccessor.QuerySamplerType(cbufOffset, cbufSlot);
@ -234,7 +232,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
} }
else if (texOp.Type == SamplerType.TextureBuffer && newType == SamplerType.Texture1D) else if (texOp.Type == SamplerType.TextureBuffer && newType == SamplerType.Texture1D)
{ {
int coordsCount = 1; int coordsCount = 2;
if (InstEmit.Sample1DAs2D) if (InstEmit.Sample1DAs2D)
{ {
@ -255,7 +253,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
} }
} }
config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset); int binding = config.ResourceManager.GetTextureOrImageBinding(
texOp.Inst,
texOp.Type,
texOp.Format,
texOp.Flags & ~TextureFlags.Bindless,
cbufSlot,
cbufOffset);
texOp.SetBinding(binding);
} }
} }
} }

View file

@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{ {
static class BindlessToIndexed static class BindlessToIndexed
{ {
private const int NvnTextureBufferIndex = 2;
public static void RunPass(BasicBlock block, ShaderConfig config) public static void RunPass(BasicBlock block, ShaderConfig config)
{ {
// We can turn a bindless texture access into a indexed access, // We can turn a bindless texture access into a indexed access,
@ -43,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (ldcSrc0.Type != OperandType.Constant || if (ldcSrc0.Type != OperandType.Constant ||
!config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) || !config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) ||
src0CbufSlot != 2) src0CbufSlot != NvnTextureBufferIndex)
{ {
continue; continue;
} }
@ -102,8 +104,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle) private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle)
{ {
texOp.TurnIntoIndexed(handle); int binding = config.ResourceManager.GetTextureOrImageBinding(
config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle); texOp.Inst,
texOp.Type | SamplerType.Indexed,
texOp.Format,
texOp.Flags & ~TextureFlags.Bindless,
NvnTextureBufferIndex,
handle);
texOp.TurnIntoIndexed(binding);
} }
} }
} }

View file

@ -1,4 +1,5 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -13,9 +14,13 @@ namespace Ryujinx.Graphics.Shader.Translation
private const int DefaultLocalMemorySize = 128; private const int DefaultLocalMemorySize = 128;
private const int DefaultSharedMemorySize = 4096; private const int DefaultSharedMemorySize = 4096;
private static readonly string[] _stagePrefixes = { "cp", "vp", "tcp", "tep", "gp", "fp" }; // TODO: Non-hardcoded array size.
public const int SamplerArraySize = 4;
private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
private readonly IGpuAccessor _gpuAccessor; private readonly IGpuAccessor _gpuAccessor;
private readonly ShaderStage _stage;
private readonly string _stagePrefix; private readonly string _stagePrefix;
private readonly int[] _cbSlotToBindingMap; private readonly int[] _cbSlotToBindingMap;
@ -27,6 +32,19 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly HashSet<int> _usedConstantBufferBindings; private readonly HashSet<int> _usedConstantBufferBindings;
private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
private struct TextureMeta
{
public int Binding;
public bool AccurateType;
public SamplerType Type;
public TextureUsageFlags UsageFlags;
}
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
public int LocalMemoryId { get; private set; } public int LocalMemoryId { get; private set; }
public int SharedMemoryId { get; private set; } public int SharedMemoryId { get; private set; }
@ -36,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
_gpuAccessor = gpuAccessor; _gpuAccessor = gpuAccessor;
Properties = properties; Properties = properties;
_stage = stage;
_stagePrefix = GetShaderStagePrefix(stage); _stagePrefix = GetShaderStagePrefix(stage);
_cbSlotToBindingMap = new int[18]; _cbSlotToBindingMap = new int[18];
@ -48,7 +67,10 @@ namespace Ryujinx.Graphics.Shader.Translation
_usedConstantBufferBindings = new HashSet<int>(); _usedConstantBufferBindings = new HashSet<int>();
properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); _usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
properties.AddOrUpdateConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType()));
LocalMemoryId = -1; LocalMemoryId = -1;
SharedMemoryId = -1; SharedMemoryId = -1;
@ -166,6 +188,198 @@ namespace Ryujinx.Graphics.Shader.Translation
return false; return false;
} }
public int GetTextureOrImageBinding(
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int cbufSlot,
int handle)
{
inst &= Instruction.Mask;
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize;
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
bool coherent = flags.HasFlag(TextureFlags.Coherent);
if (!isImage)
{
format = TextureFormat.Unknown;
}
int binding = GetTextureOrImageBinding(cbufSlot, handle, type, format, isImage, intCoords, isWrite, accurateType, coherent);
_gpuAccessor.RegisterTexture(handle, cbufSlot);
return binding;
}
private int GetTextureOrImageBinding(
int cbufSlot,
int handle,
SamplerType type,
TextureFormat format,
bool isImage,
bool intCoords,
bool write,
bool accurateType,
bool coherent)
{
var dimensions = type.GetDimensions();
var isIndexed = type.HasFlag(SamplerType.Indexed);
var dict = isImage ? _usedImages : _usedTextures;
var usageFlags = TextureUsageFlags.None;
if (intCoords)
{
usageFlags |= TextureUsageFlags.NeedsScaleValue;
var canScale = _stage.SupportsRenderScale() && !isIndexed && !write && dimensions == 2;
if (!canScale)
{
// Resolution scaling cannot be applied to this texture right now.
// Flag so that we know to blacklist scaling on related textures when binding them.
usageFlags |= TextureUsageFlags.ResScaleUnsupported;
}
}
if (write)
{
usageFlags |= TextureUsageFlags.ImageStore;
}
if (coherent)
{
usageFlags |= TextureUsageFlags.ImageCoherent;
}
int arraySize = isIndexed ? SamplerArraySize : 1;
int firstBinding = -1;
for (int layer = 0; layer < arraySize; layer++)
{
var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format);
var meta = new TextureMeta()
{
AccurateType = accurateType,
Type = type,
UsageFlags = usageFlags
};
int binding;
if (dict.TryGetValue(info, out var existingMeta))
{
dict[info] = MergeTextureMeta(meta, existingMeta);
binding = existingMeta.Binding;
}
else
{
bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer;
binding = isImage
? _gpuAccessor.QueryBindingImage(dict.Count, isBuffer)
: _gpuAccessor.QueryBindingTexture(dict.Count, isBuffer);
meta.Binding = binding;
dict.Add(info, meta);
}
string nameSuffix;
if (isImage)
{
nameSuffix = cbufSlot < 0
? $"i_tcb_{handle:X}_{format.ToGlslFormat()}"
: $"i_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
}
else
{
nameSuffix = cbufSlot < 0 ? $"t_tcb_{handle:X}" : $"t_cb{cbufSlot}_{handle:X}";
}
var definition = new TextureDefinition(
isImage ? 3 : 2,
binding,
$"{_stagePrefix}_{nameSuffix}",
meta.Type,
info.Format,
meta.UsageFlags);
if (isImage)
{
Properties.AddOrUpdateImage(binding, definition);
}
else
{
Properties.AddOrUpdateTexture(binding, definition);
}
if (layer == 0)
{
firstBinding = binding;
}
}
return firstBinding;
}
private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta)
{
meta.Binding = existingMeta.Binding;
meta.UsageFlags |= existingMeta.UsageFlags;
// If the texture we have has inaccurate type information, then
// we prefer the most accurate one.
if (existingMeta.AccurateType)
{
meta.AccurateType = true;
meta.Type = existingMeta.Type;
}
return meta;
}
public void SetUsageFlagsForTextureQuery(int binding, SamplerType type)
{
TextureInfo selectedInfo = default;
TextureMeta selectedMeta = default;
bool found = false;
foreach ((TextureInfo info, TextureMeta meta) in _usedTextures)
{
if (meta.Binding == binding)
{
selectedInfo = info;
selectedMeta = meta;
found = true;
break;
}
}
if (found)
{
selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue;
var dimensions = type.GetDimensions();
var isIndexed = type.HasFlag(SamplerType.Indexed);
var canScale = _stage.SupportsRenderScale() && !isIndexed && dimensions == 2;
if (!canScale)
{
// Resolution scaling cannot be applied to this texture right now.
// Flag so that we know to blacklist scaling on related textures when binding them.
selectedMeta.UsageFlags |= TextureUsageFlags.ResScaleUnsupported;
}
_usedTextures[selectedInfo] = selectedMeta;
}
}
public void SetUsedConstantBufferBinding(int binding) public void SetUsedConstantBufferBinding(int binding)
{ {
_usedConstantBufferBindings.Add(binding); _usedConstantBufferBindings.Add(binding);
@ -208,10 +422,8 @@ namespace Ryujinx.Graphics.Shader.Translation
if (binding >= 0) if (binding >= 0)
{ {
(int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key);
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset) BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None;
{ descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags);
Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None,
};
} }
} }
@ -223,6 +435,64 @@ namespace Ryujinx.Graphics.Shader.Translation
return descriptors; return descriptors;
} }
public TextureDescriptor[] GetTextureDescriptors()
{
return GetDescriptors(_usedTextures, _usedTextures.Count);
}
public TextureDescriptor[] GetImageDescriptors()
{
return GetDescriptors(_usedImages, _usedImages.Count);
}
private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary<TextureInfo, TextureMeta> usedResources, int count)
{
TextureDescriptor[] descriptors = new TextureDescriptor[count];
int descriptorIndex = 0;
foreach ((TextureInfo info, TextureMeta meta) in usedResources)
{
descriptors[descriptorIndex++] = new TextureDescriptor(
meta.Binding,
meta.Type,
info.Format,
info.CbufSlot,
info.Handle,
meta.UsageFlags);
}
return descriptors;
}
public (int, int) GetCbufSlotAndHandleForTexture(int binding)
{
foreach ((TextureInfo info, TextureMeta meta) in _usedTextures)
{
if (meta.Binding == binding)
{
return (info.CbufSlot, info.Handle);
}
}
throw new ArgumentException($"Binding {binding} is invalid.");
}
private static int FindDescriptorIndex(TextureDescriptor[] array, int binding)
{
return Array.FindIndex(array, x => x.Binding == binding);
}
public int FindTextureDescriptorIndex(int binding)
{
return FindDescriptorIndex(GetTextureDescriptors(), binding);
}
public int FindImageDescriptorIndex(int binding)
{
return FindDescriptorIndex(GetImageDescriptors(), binding);
}
private void AddNewConstantBuffer(int binding, string name) private void AddNewConstantBuffer(int binding, string name)
{ {
StructureType type = new(new[] StructureType type = new(new[]
@ -230,7 +500,7 @@ namespace Ryujinx.Graphics.Shader.Translation
new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16),
}); });
Properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type)); Properties.AddOrUpdateConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
} }
private void AddNewStorageBuffer(int binding, string name) private void AddNewStorageBuffer(int binding, string name)
@ -240,7 +510,7 @@ namespace Ryujinx.Graphics.Shader.Translation
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), new StructureField(AggregateType.Array | AggregateType.U32, "data", 0),
}); });
Properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type)); Properties.AddOrUpdateStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type));
} }
public static string GetShaderStagePrefix(ShaderStage stage) public static string GetShaderStagePrefix(ShaderStage stage)

View file

@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (texOp.Inst == Instruction.TextureSample) if (texOp.Inst == Instruction.TextureSample)
{ {
node = InsertCoordNormalization(node, config); node = InsertCoordNormalization(hfm, node, config);
node = InsertCoordGatherBias(node, config); node = InsertCoordGatherBias(node, config);
node = InsertConstOffsets(node, config); node = InsertConstOffsets(node, config);
@ -285,8 +285,8 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale);
int samplerIndex = isImage int samplerIndex = isImage
? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp) ? config.ResourceManager.GetTextureDescriptors().Length + config.ResourceManager.FindImageDescriptorIndex(texOp.Binding)
: config.FindTextureDescriptorIndex(texOp); : config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding);
for (int index = 0; index < coordsCount; index++) for (int index = 0; index < coordsCount; index++)
{ {
@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Shader.Translation
TypeSupportsScale(texOp.Type)) TypeSupportsScale(texOp.Type))
{ {
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale); int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale);
int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true); int samplerIndex = config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding);
for (int index = texOp.DestsCount - 1; index >= 0; index--) for (int index = texOp.DestsCount - 1; index >= 0; index--)
{ {
@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return (type & SamplerType.Mask) == SamplerType.Texture2D; return (type & SamplerType.Mask) == SamplerType.Texture2D;
} }
private static LinkedListNode<INode> InsertCoordNormalization(LinkedListNode<INode> node, ShaderConfig config) private static LinkedListNode<INode> InsertCoordNormalization(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
{ {
// Emulate non-normalized coordinates by normalizing the coordinates on the shader. // Emulate non-normalized coordinates by normalizing the coordinates on the shader.
// Without normalization, the coordinates are expected to the in the [0, W or H] range, // Without normalization, the coordinates are expected to the in the [0, W or H] range,
@ -378,9 +378,17 @@ namespace Ryujinx.Graphics.Shader.Translation
TextureOperation texOp = (TextureOperation)node.Value; TextureOperation texOp = (TextureOperation)node.Value;
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
if (isBindless)
{
return node;
}
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot); (int cbufSlot, int handle) = config.ResourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding);
bool isCoordNormalized = config.GpuAccessor.QueryTextureCoordNormalized(handle, cbufSlot);
if (isCoordNormalized || intCoords) if (isCoordNormalized || intCoords)
{ {
@ -411,18 +419,17 @@ namespace Ryujinx.Graphics.Shader.Translation
texSizeSources = new Operand[] { Const(0) }; texSizeSources = new Operand[] { Const(0) };
} }
node.List.AddBefore(node, new TextureOperation( LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
Instruction.TextureSize, Instruction.TextureSize,
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.CbufSlot, texOp.Binding,
texOp.Handle,
index, index,
new[] { coordSize }, new[] { coordSize },
texSizeSources)); texSizeSources));
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); config.ResourceManager.SetUsageFlagsForTextureQuery(texOp.Binding, texOp.Type);
Operand source = texOp.GetSource(coordsIndex + index); Operand source = texOp.GetSource(coordsIndex + index);
@ -431,6 +438,8 @@ namespace Ryujinx.Graphics.Shader.Translation
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize))); node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize)));
texOp.SetSource(coordsIndex + index, coordNormalized); texOp.SetSource(coordsIndex + index, coordNormalized);
InsertTextureSizeUnscale(hfm, textureSizeNode, config);
} }
return node; return node;
@ -491,14 +500,11 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.CbufSlot, texOp.Binding,
texOp.Handle,
index, index,
new[] { coordSize }, new[] { coordSize },
texSizeSources)); texSizeSources));
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
node.List.AddBefore(node, new Operation( node.List.AddBefore(node, new Operation(
Instruction.FP32 | Instruction.Multiply, Instruction.FP32 | Instruction.Multiply,
scaledSize, scaledSize,
@ -686,8 +692,6 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int index = 0; index < coordsCount; index++) for (int index = 0; index < coordsCount; index++)
{ {
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
Operand offset = Local(); Operand offset = Local();
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
@ -712,8 +716,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot, texOp.Binding,
texOp.Handle,
1, 1,
new[] { dests[destIndex++] }, new[] { dests[destIndex++] },
newSources); newSources);
@ -744,8 +747,6 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int index = 0; index < coordsCount; index++) for (int index = 0; index < coordsCount; index++)
{ {
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
Operand offset = Local(); Operand offset = Local();
Operand intOffset = offsets[index]; Operand intOffset = offsets[index];
@ -771,8 +772,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.CbufSlot, texOp.Binding,
texOp.Handle,
componentIndex, componentIndex,
dests, dests,
sources); sources);
@ -806,8 +806,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.CbufSlot, texOp.Binding,
texOp.Handle,
0, 0,
new[] { lod }, new[] { lod },
lodSources)); lodSources));
@ -832,8 +831,7 @@ namespace Ryujinx.Graphics.Shader.Translation
texOp.Type, texOp.Type,
texOp.Format, texOp.Format,
texOp.Flags, texOp.Flags,
texOp.CbufSlot, texOp.Binding,
texOp.Handle,
index, index,
new[] { texSizes[index] }, new[] { texSizes[index] },
texSizeSources)); texSizeSources));
@ -853,7 +851,9 @@ namespace Ryujinx.Graphics.Shader.Translation
return node; return node;
} }
TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle, texOp.CbufSlot); (int cbufSlot, int handle) = config.ResourceManager.GetCbufSlotAndHandleForTexture(texOp.Binding);
TextureFormat format = config.GpuAccessor.QueryTextureFormat(handle, cbufSlot);
int maxPositive = format switch int maxPositive = format switch
{ {

View file

@ -2,16 +2,12 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Numerics; using System.Numerics;
namespace Ryujinx.Graphics.Shader.Translation namespace Ryujinx.Graphics.Shader.Translation
{ {
class ShaderConfig class ShaderConfig
{ {
// TODO: Non-hardcoded array size.
public const int SamplerArraySize = 4;
private const int ThreadsPerWarp = 32; private const int ThreadsPerWarp = 32;
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
@ -110,20 +106,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 NextInputAttributesComponents { get; private set; }
public UInt128 ThisInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; }
private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
private struct TextureMeta
{
public bool AccurateType;
public SamplerType Type;
public TextureUsageFlags UsageFlags;
}
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
private TextureDescriptor[] _cachedTextureDescriptors;
private TextureDescriptor[] _cachedImageDescriptors;
public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize)
{ {
Stage = stage; Stage = stage;
@ -141,9 +123,6 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedInputAttributesPerPatch = new HashSet<int>(); UsedInputAttributesPerPatch = new HashSet<int>();
UsedOutputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>();
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled()) if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled())
@ -156,7 +135,7 @@ namespace Ryujinx.Graphics.Shader.Translation
BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct); BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct);
Properties.AddStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); Properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer);
StructureType tfeDataStruct = new(new StructureField[] StructureType tfeDataStruct = new(new StructureField[]
{ {
@ -167,7 +146,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int binding = Constants.TfeBufferBaseBinding + i; int binding = Constants.TfeBufferBaseBinding + i;
BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct);
Properties.AddStorageBuffer(binding, tfeDataBuffer); Properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer);
} }
} }
} }
@ -443,22 +422,6 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedInputAttributes |= other.UsedInputAttributes; UsedInputAttributes |= other.UsedInputAttributes;
UsedOutputAttributes |= other.UsedOutputAttributes; UsedOutputAttributes |= other.UsedOutputAttributes;
foreach (var kv in other._usedTextures)
{
if (!_usedTextures.TryAdd(kv.Key, kv.Value))
{
_usedTextures[kv.Key] = MergeTextureMeta(kv.Value, _usedTextures[kv.Key]);
}
}
foreach (var kv in other._usedImages)
{
if (!_usedImages.TryAdd(kv.Key, kv.Value))
{
_usedImages[kv.Key] = MergeTextureMeta(kv.Value, _usedImages[kv.Key]);
}
}
} }
public void SetLayerOutputAttribute(int attr) public void SetLayerOutputAttribute(int attr)
@ -642,196 +605,13 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedFeatures |= flags; UsedFeatures |= flags;
} }
public void SetUsedTexture(
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int cbufSlot,
int handle)
{
inst &= Instruction.Mask;
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize;
bool coherent = flags.HasFlag(TextureFlags.Coherent);
if (isImage)
{
SetUsedTextureOrImage(_usedImages, cbufSlot, handle, type, format, true, isWrite, false, coherent);
}
else
{
bool intCoords = flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, intCoords, false, accurateType, coherent);
}
GpuAccessor.RegisterTexture(handle, cbufSlot);
}
private void SetUsedTextureOrImage(
Dictionary<TextureInfo, TextureMeta> dict,
int cbufSlot,
int handle,
SamplerType type,
TextureFormat format,
bool intCoords,
bool write,
bool accurateType,
bool coherent)
{
var dimensions = type.GetDimensions();
var isIndexed = type.HasFlag(SamplerType.Indexed);
var usageFlags = TextureUsageFlags.None;
if (intCoords)
{
usageFlags |= TextureUsageFlags.NeedsScaleValue;
var canScale = Stage.SupportsRenderScale() && !isIndexed && !write && dimensions == 2;
if (!canScale)
{
// Resolution scaling cannot be applied to this texture right now.
// Flag so that we know to blacklist scaling on related textures when binding them.
usageFlags |= TextureUsageFlags.ResScaleUnsupported;
}
}
if (write)
{
usageFlags |= TextureUsageFlags.ImageStore;
}
if (coherent)
{
usageFlags |= TextureUsageFlags.ImageCoherent;
}
int arraySize = isIndexed ? SamplerArraySize : 1;
for (int layer = 0; layer < arraySize; layer++)
{
var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format);
var meta = new TextureMeta()
{
AccurateType = accurateType,
Type = type,
UsageFlags = usageFlags,
};
if (dict.TryGetValue(info, out var existingMeta))
{
dict[info] = MergeTextureMeta(meta, existingMeta);
}
else
{
dict.Add(info, meta);
}
}
}
private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta)
{
meta.UsageFlags |= existingMeta.UsageFlags;
// If the texture we have has inaccurate type information, then
// we prefer the most accurate one.
if (existingMeta.AccurateType)
{
meta.AccurateType = true;
meta.Type = existingMeta.Type;
}
return meta;
}
public TextureDescriptor[] GetTextureDescriptors()
{
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture);
}
public TextureDescriptor[] GetImageDescriptors()
{
return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, GpuAccessor.QueryBindingImage);
}
private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int, bool, int> getBindingCallback)
{
var descriptors = new TextureDescriptor[dict.Count];
int i = 0;
foreach (var kv in dict.OrderBy(x => x.Key.Indexed).ThenBy(x => x.Key.Handle))
{
var info = kv.Key;
var meta = kv.Value;
bool isBuffer = (meta.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
int binding = getBindingCallback(i, isBuffer);
descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle);
descriptors[i].SetFlag(meta.UsageFlags);
i++;
}
return descriptors;
}
public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp)
{
TextureDescriptor[] descriptors = GetTextureDescriptors();
for (int i = 0; i < descriptors.Length; i++)
{
var descriptor = descriptors[i];
if (descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return descriptor;
}
}
return default;
}
private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false)
{
for (int i = 0; i < array.Length; i++)
{
var descriptor = array[i];
if ((descriptor.Type == texOp.Type || ignoreType) &&
descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return i;
}
}
return -1;
}
public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false)
{
return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType);
}
public int FindImageDescriptorIndex(TextureOperation texOp)
{
return FindDescriptorIndex(GetImageDescriptors(), texOp);
}
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None) public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
{ {
return new ShaderProgramInfo( return new ShaderProgramInfo(
ResourceManager.GetConstantBufferDescriptors(), ResourceManager.GetConstantBufferDescriptors(),
ResourceManager.GetStorageBufferDescriptors(), ResourceManager.GetStorageBufferDescriptors(),
GetTextureDescriptors(), ResourceManager.GetTextureDescriptors(),
GetImageDescriptors(), ResourceManager.GetImageDescriptors(),
identification, identification,
GpLayerInputAttribute, GpLayerInputAttribute,
Stage, Stage,