diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 95a0a6bd..672b3b8d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; 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 SharedDataFileName = "shared.data"; diff --git a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs index d1da9539..d8cad1df 100644 --- a/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader public readonly byte Slot; public readonly byte SbCbSlot; public readonly ushort SbCbOffset; - public BufferUsageFlags Flags; + public readonly BufferUsageFlags Flags; public BufferDescriptor(int binding, int slot) { @@ -16,25 +16,16 @@ namespace Ryujinx.Graphics.Shader Slot = (byte)slot; SbCbSlot = 0; SbCbOffset = 0; - 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; Slot = (byte)slot; SbCbSlot = (byte)sbCbSlot; SbCbOffset = (ushort)sbCbOffset; - - Flags = BufferUsageFlags.None; - } - - public BufferDescriptor SetFlag(BufferUsageFlags flag) - { - Flags |= flag; - - return this; + Flags = flags; } } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 94b850e7..2370b49f 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -75,22 +75,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareMemories(context, context.Config.Properties.LocalMemories.Values, isShared: false); DeclareMemories(context, context.Config.Properties.SharedMemories.Values, isShared: true); - - var textureDescriptors = context.Config.GetTextureDescriptors(); - if (textureDescriptors.Length != 0) - { - DeclareSamplers(context, textureDescriptors); - - context.AppendLine(); - } - - var imageDescriptors = context.Config.GetImageDescriptors(); - if (imageDescriptors.Length != 0) - { - DeclareImages(context, imageDescriptors); - - context.AppendLine(); - } + DeclareSamplers(context, context.Config.Properties.Textures.Values); + DeclareImages(context, context.Config.Properties.Images.Values); 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 definitions) { 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) { - arraySize = ShaderConfig.SamplerArraySize; + arraySize = ResourceManager.SamplerArraySize; } else if (--arraySize != 0) { continue; } + + indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]"; } - string indexExpr = NumberFormatter.FormatInt(arraySize); - - string samplerName = OperandManager.GetSamplerName( - context.Config.Stage, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Type.HasFlag(SamplerType.Indexed), - indexExpr); - - string samplerTypeName = descriptor.Type.ToGlslSamplerType(); + string samplerTypeName = definition.Type.ToGlslSamplerType(); string layout = string.Empty; 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 definitions) { 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) { - arraySize = ShaderConfig.SamplerArraySize; + arraySize = ResourceManager.SamplerArraySize; } else if (--arraySize != 0) { continue; } + + indexExpr = $"[{NumberFormatter.FormatInt(arraySize)}]"; } - string indexExpr = NumberFormatter.FormatInt(arraySize); + string imageTypeName = definition.Type.ToGlslImageType(definition.Format.GetComponentType()); - string imageName = OperandManager.GetImageName( - 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)) + if (definition.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) { imageTypeName = "coherent " + imageTypeName; } - string layout = descriptor.Format.ToGlslFormat(); + string layout = definition.Format.ToGlslFormat(); if (!string.IsNullOrEmpty(layout)) { @@ -451,10 +428,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl 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};"); } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 842228ed..54bf9aeb 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -4,9 +4,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public const string LocalNamePrefix = "temp"; - public const string SamplerNamePrefix = "tex"; - public const string ImageNamePrefix = "img"; - public const string PerPatchAttributePrefix = "patch_attr_"; public const string IAttributePrefix = "in_attr"; public const string OAttributePrefix = "out_attr"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index e0faed29..7e6d8bb5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = Src(AggregateType.S32); } - string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); + string imageName = GetImageName(context.Config, texOp, indexExpr); texCallBuilder.Append('('); texCallBuilder.Append(imageName); @@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions 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; @@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions indexExpr = Src(AggregateType.S32); } - string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); + string samplerName = GetSamplerName(context.Config, texOp, indexExpr); texCall += "(" + samplerName; @@ -538,7 +538,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions 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) { @@ -546,8 +546,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else { - TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp); - bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer; + context.Config.Properties.Textures.TryGetValue(texOp.Binding, out TextureDefinition definition); + bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; string texCall; if (hasLod) @@ -715,6 +715,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions 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) { return $".{"rgba".AsSpan(index, 1)}"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 0ca3b55f..17ffad9a 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -11,9 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { class OperandManager { - private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; - - private readonly Dictionary _locals; + private Dictionary _locals; 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) { return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs index 9956e90a..d4f49045 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs @@ -28,9 +28,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv public Dictionary StorageBuffers { get; } = new Dictionary(); public Dictionary LocalMemories { get; } = new Dictionary(); public Dictionary SharedMemories { get; } = new Dictionary(); - public Dictionary SamplersTypes { get; } = new Dictionary(); - public Dictionary Samplers { get; } = new Dictionary(); - public Dictionary Images { get; } = new Dictionary(); + public Dictionary SamplersTypes { get; } = new Dictionary(); + public Dictionary Samplers { get; } = new Dictionary(); + public Dictionary Images { get; } = new Dictionary(); public Dictionary Inputs { get; } = new Dictionary(); public Dictionary Outputs { get; } = new Dictionary(); public Dictionary InputsPerPatch { get; } = new Dictionary(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index da1e385a..2c849cd4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -72,8 +72,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values); DeclareMemories(context, context.Config.Properties.LocalMemories, context.LocalMemories, StorageClass.Private); DeclareMemories(context, context.Config.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup); - DeclareSamplers(context, context.Config.GetTextureDescriptors()); - DeclareImages(context, context.Config.GetImageDescriptors()); + DeclareSamplers(context, context.Config.Properties.Textures.Values); + DeclareImages(context, context.Config.Properties.Images.Values); DeclareInputsAndOutputs(context, info); } @@ -110,6 +110,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv 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 alignmentMask = alignment - 1; int offset = 0; @@ -163,7 +164,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv var variable = context.Variable(pointerType, StorageClass.Uniform); 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.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 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)) - { - continue; - } - - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0; - - var dim = (descriptor.Type & SamplerType.Mask) switch + var dim = (sampler.Type & SamplerType.Mask) switch { SamplerType.Texture1D => Dim.Dim1D, SamplerType.Texture2D => Dim.Dim2D, SamplerType.Texture3D => Dim.Dim3D, SamplerType.TextureCube => Dim.Cube, 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( context.TypeFP32(), dim, - descriptor.Type.HasFlag(SamplerType.Shadow), - descriptor.Type.HasFlag(SamplerType.Array), - descriptor.Type.HasFlag(SamplerType.Multisample), + sampler.Type.HasFlag(SamplerType.Shadow), + sampler.Type.HasFlag(SamplerType.Array), + sampler.Type.HasFlag(SamplerType.Multisample), 1, ImageFormat.Unknown); - var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}"; - var sampledImageType = context.TypeSampledImage(imageType); var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType); var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant); - context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable)); - context.SamplersTypes.Add(meta, descriptor.Type); + context.Samplers.Add(sampler.Binding, (imageType, sampledImageType, sampledImageVariable)); + 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.Binding, (LiteralInteger)descriptor.Binding); + context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)sampler.Binding); context.AddGlobalVariable(sampledImageVariable); } } - private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors) + private static void DeclareImages(CodeGenContext context, IEnumerable 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)) - { - continue; - } - - int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0; - - var dim = GetDim(descriptor.Type); + var dim = GetDim(image.Type); var imageType = context.TypeImage( - context.GetType(meta.Format.GetComponentType()), + context.GetType(image.Format.GetComponentType()), dim, - descriptor.Type.HasFlag(SamplerType.Shadow), - descriptor.Type.HasFlag(SamplerType.Array), - descriptor.Type.HasFlag(SamplerType.Multisample), + image.Type.HasFlag(SamplerType.Shadow), + image.Type.HasFlag(SamplerType.Array), + image.Type.HasFlag(SamplerType.Multisample), AccessQualifier.ReadWrite, - GetImageFormat(meta.Format)); - - var nameSuffix = meta.CbufSlot < 0 ? - $"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" : - $"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}"; + GetImageFormat(image.Format)); var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType); 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.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); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index a53b40b2..9489397b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -657,7 +657,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv 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); @@ -742,7 +742,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv 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 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 (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); @@ -908,9 +908,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv pCoords = Src(AggregateType.FP32); } - var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - - var (_, sampledImageType, sampledImageVariable) = context.Samplers[meta]; + (_, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; 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 meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - - var (imageType, sampledImageType, sampledImageVariable) = context.Samplers[meta]; + (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; var image = context.Load(sampledImageType, sampledImageVariable); @@ -1592,9 +1588,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv context.GetS32(texOp.GetSource(0)); } - var meta = new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format); - - (SpvInstruction imageType, SpvInstruction sampledImageType, SpvInstruction sampledImageVariable) = context.Samplers[meta]; + (var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding]; var image = context.Load(sampledImageType, sampledImageVariable); image = context.Image(imageType, image); @@ -1605,7 +1599,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv } else { - var type = context.SamplersTypes[meta]; + var type = context.SamplersTypes[texOp.Binding]; bool hasLod = !type.HasFlag(SamplerType.Multisample) && type != SamplerType.TextureBuffer; int dimensions = (type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : type.GetDimensions(); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs deleted file mode 100644 index 56ea9a2a..00000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Spirv -{ - readonly record struct TextureMeta(int CbufSlot, int Handle, TextureFormat Format); -} diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs index 78fc313d..0b929307 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs @@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions 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 sourcesList = new(); @@ -277,17 +277,17 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - TextureOperation operation = context.CreateTextureOperation( + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, flags, - imm, - 0, - new[] { destOperand }, - sources); + TextureOperation.DefaultCbufSlot, + imm); - context.Add(operation); + Operand res = context.ImageAtomic(type, format, flags, binding, sources); + + context.Copy(d, res); } private static void EmitSuld( @@ -383,21 +383,17 @@ namespace Ryujinx.Graphics.Shader.Instructions 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, type, + format, flags, - handle, - (int)componentMask, - dests, - sources); + TextureOperation.DefaultCbufSlot, + handle); - if (!isBindless) - { - operation.Format = context.Config.GetTextureFormat(handle); - } - - context.Add(operation); + context.ImageLoad(type, format, flags, binding, (int)componentMask, dests, sources); } else { @@ -430,17 +426,17 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - TextureOperation operation = context.CreateTextureOperation( + TextureFormat format = GetTextureFormat(size); + + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageLoad, type, - GetTextureFormat(size), + format, flags, - handle, - compMask, - dests, - sources); + TextureOperation.DefaultCbufSlot, + handle); - context.Add(operation); + context.ImageLoad(type, format, flags, binding, compMask, dests, sources); switch (size) { @@ -552,17 +548,15 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Bindless; } - TextureOperation operation = context.CreateTextureOperation( + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageAtomic, type, format, flags, - imm, - 0, - null, - sources); + TextureOperation.DefaultCbufSlot, + imm); - context.Add(operation); + context.ImageAtomic(type, format, flags, binding, sources); } private static void EmitSust( @@ -681,17 +675,15 @@ namespace Ryujinx.Graphics.Shader.Instructions flags |= TextureFlags.Coherent; } - TextureOperation operation = context.CreateTextureOperation( + int binding = isBindless ? 0 : context.Config.ResourceManager.GetTextureOrImageBinding( Instruction.ImageStore, type, format, flags, - handle, - 0, - null, - sources); + TextureOperation.DefaultCbufSlot, + handle); - context.Add(operation); + context.ImageStore(type, format, flags, binding, sources); } private static int GetComponentSizeInBytesLog2(SuatomSize size) diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 3701325e..7d3d22d8 100644 --- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -324,16 +324,7 @@ namespace Ryujinx.Graphics.Shader.Instructions int handle = !isBindless ? imm : 0; - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, handle, componentMask, dests, sources); } private static void EmitTexs( @@ -657,16 +648,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, handle, componentMask, dests, sources); if (isF16) { @@ -812,18 +794,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - int handle = imm; - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, imm, componentMask, dests, sources); } private static void EmitTmml( @@ -913,15 +884,21 @@ namespace Ryujinx.Graphics.Shader.Instructions 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++) { if ((compMask & 1) != 0) { - Operand destOperand = GetDest(); + Operand d = GetDest(); - if (destOperand == null) + if (d == null) { break; } @@ -930,28 +907,18 @@ namespace Ryujinx.Graphics.Shader.Instructions if (compIndex >= 2) { context.Add(new CommentNode("Unsupported component z or w found")); - context.Copy(destOperand, Const(0)); + context.Copy(d, Const(0)); } 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( - Instruction.Lod, - type, - flags, - handle, - compIndex ^ 1, // The instruction component order is the inverse of GLSL's. - new[] { tempDest }, - sources); + res = context.FPMultiply(res, ConstF(256.0f)); - context.Add(operation); + Operand fixedPointValue = context.FP32ConvertToS32(res); - tempDest = context.FPMultiply(tempDest, ConstF(256.0f)); - - Operand fixedPointValue = context.FP32ConvertToS32(tempDest); - - context.Copy(destOperand, fixedPointValue); + context.Copy(d, fixedPointValue); } } } @@ -1081,18 +1048,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Array.Resize(ref dests, outputIndex); } - int handle = imm; - - TextureOperation operation = context.CreateTextureOperation( - Instruction.TextureSample, - type, - flags, - handle, - componentMask, - dests, - sources); - - context.Add(operation); + EmitTextureSample(context, type, flags, imm, componentMask, dests, sources); } private static void EmitTxq( @@ -1111,10 +1067,6 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Config.SetUsedFeature(FeatureFlags.IntegerSampling); - // TODO: Validate and use query. - Instruction inst = Instruction.TextureSize; - TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None; - Operand Ra() { if (srcA > RegisterConsts.RegisterZeroIndex) @@ -1157,31 +1109,55 @@ namespace Ryujinx.Graphics.Shader.Instructions 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++) { if ((compMask & 1) != 0) { - Operand destOperand = GetDest(); + Operand d = GetDest(); - if (destOperand == null) + if (d == null) { break; } - TextureOperation operation = context.CreateTextureOperation( - inst, - type, - flags, - imm, - compIndex, - new[] { destOperand }, - sources); + // TODO: Validate and use query parameter. + Operand res = context.TextureSize(type, flags, binding, compIndex, 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) { return dimensions switch diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index b467fe53..fa5550a6 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -8,16 +8,14 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public TextureFormat Format { get; set; } public TextureFlags Flags { get; private set; } - public int CbufSlot { get; private set; } - public int Handle { get; private set; } + public int Binding { get; private set; } public TextureOperation( Instruction inst, SamplerType type, TextureFormat format, TextureFlags flags, - int cbufSlot, - int handle, + int binding, int compIndex, Operand[] dests, Operand[] sources) : base(inst, compIndex, dests, sources) @@ -25,30 +23,17 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Type = type; Format = format; Flags = flags; - CbufSlot = cbufSlot; - Handle = handle; + Binding = binding; } - public TextureOperation( - 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) + public void TurnIntoIndexed(int binding) { Type |= SamplerType.Indexed; 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) { @@ -57,8 +42,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation RemoveSource(0); } - CbufSlot = cbufSlot; - Handle = handle; + Binding = binding; } public void SetLodLevelFlag() diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 4ff2035a..3970df1e 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -8,24 +8,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public TextureFormat Format { get; } public TextureFlags Flags { get; } - public int CbufSlot { get; } - public int Handle { get; } + public int Binding { get; } public AstTextureOperation( Instruction inst, SamplerType type, TextureFormat format, TextureFlags flags, - int cbufSlot, - int handle, + int binding, int index, params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) { Type = type; Format = format; Flags = flags; - CbufSlot = cbufSlot; - Handle = handle; + Binding = binding; } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs index 1da5cb65..048a260a 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs @@ -6,11 +6,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { private readonly Dictionary _constantBuffers; private readonly Dictionary _storageBuffers; + private readonly Dictionary _textures; + private readonly Dictionary _images; private readonly Dictionary _localMemories; private readonly Dictionary _sharedMemories; public IReadOnlyDictionary ConstantBuffers => _constantBuffers; public IReadOnlyDictionary StorageBuffers => _storageBuffers; + public IReadOnlyDictionary Textures => _textures; + public IReadOnlyDictionary Images => _images; public IReadOnlyDictionary LocalMemories => _localMemories; public IReadOnlyDictionary SharedMemories => _sharedMemories; @@ -18,20 +22,32 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { _constantBuffers = new Dictionary(); _storageBuffers = new Dictionary(); + _textures = new Dictionary(); + _images = new Dictionary(); _localMemories = new Dictionary(); _sharedMemories = new Dictionary(); } - public void AddConstantBuffer(int binding, BufferDefinition definition) + public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition) { _constantBuffers[binding] = definition; } - public void AddStorageBuffer(int binding, BufferDefinition definition) + public void AddOrUpdateStorageBuffer(int binding, BufferDefinition 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) { int id = _localMemories.Count; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index a4e6444b..87acedf6 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -125,15 +125,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AstTextureOperation GetAstTextureOperation(TextureOperation texOp) { - return new AstTextureOperation( - inst, - texOp.Type, - texOp.Format, - texOp.Flags, - texOp.CbufSlot, - texOp.Handle, - texOp.Index, - sources); + return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.Index, sources); } int componentsCount = BitOperations.PopCount((uint)operation.Index); diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs new file mode 100644 index 00000000..ba31dae6 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs index 626faa69..58b8e5a5 100644 --- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs +++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -12,23 +12,16 @@ namespace Ryujinx.Graphics.Shader public readonly int CbufSlot; 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; Type = type; Format = format; CbufSlot = cbufSlot; HandleIndex = handleIndex; - Flags = TextureUsageFlags.None; - } - - public TextureDescriptor SetFlag(TextureUsageFlags flag) - { - Flags |= flag; - - return this; + Flags = flags; } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 9eedc3f9..08d4b915 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -115,36 +115,6 @@ namespace Ryujinx.Graphics.Shader.Translation _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) { if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index c2f1b790..c92d0583 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -604,6 +604,45 @@ namespace Ryujinx.Graphics.Shader.Translation 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) { return context.Add(fpType | Instruction.IsNan, Local(), a); @@ -666,6 +705,21 @@ namespace Ryujinx.Graphics.Shader.Translation : 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) { 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); } + 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) { return UnpackDouble2x32(context, a, 1); diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index bb25c160..bf087aff 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -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) { - texOp.SetHandle(cbufOffset, cbufSlot); - if (rewriteSamplerType) { 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) { - int coordsCount = 1; + int coordsCount = 2; 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); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index f966a4fc..4b1bf76e 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class BindlessToIndexed { + private const int NvnTextureBufferIndex = 2; + public static void RunPass(BasicBlock block, ShaderConfig config) { // 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 || !config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) || - src0CbufSlot != 2) + src0CbufSlot != NvnTextureBufferIndex) { continue; } @@ -102,8 +104,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle) { - texOp.TurnIntoIndexed(handle); - config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle); + int binding = config.ResourceManager.GetTextureOrImageBinding( + texOp.Inst, + texOp.Type | SamplerType.Indexed, + texOp.Format, + texOp.Flags & ~TextureFlags.Bindless, + NvnTextureBufferIndex, + handle); + + texOp.TurnIntoIndexed(binding); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index f3a5ba6c..5991cd25 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System; using System.Collections.Generic; @@ -13,9 +14,13 @@ namespace Ryujinx.Graphics.Shader.Translation private const int DefaultLocalMemorySize = 128; 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 ShaderStage _stage; private readonly string _stagePrefix; private readonly int[] _cbSlotToBindingMap; @@ -27,6 +32,19 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly HashSet _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 _usedTextures; + private readonly Dictionary _usedImages; + public int LocalMemoryId { get; private set; } public int SharedMemoryId { get; private set; } @@ -36,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation { _gpuAccessor = gpuAccessor; Properties = properties; + _stage = stage; _stagePrefix = GetShaderStagePrefix(stage); _cbSlotToBindingMap = new int[18]; @@ -48,7 +67,10 @@ namespace Ryujinx.Graphics.Shader.Translation _usedConstantBufferBindings = new HashSet(); - properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); + _usedTextures = new Dictionary(); + _usedImages = new Dictionary(); + + properties.AddOrUpdateConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType())); LocalMemoryId = -1; SharedMemoryId = -1; @@ -166,6 +188,198 @@ namespace Ryujinx.Graphics.Shader.Translation 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) { _usedConstantBufferBindings.Add(binding); @@ -208,10 +422,8 @@ namespace Ryujinx.Graphics.Shader.Translation if (binding >= 0) { (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); - descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset) - { - Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None, - }; + BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None; + descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags); } } @@ -223,6 +435,64 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors; } + public TextureDescriptor[] GetTextureDescriptors() + { + return GetDescriptors(_usedTextures, _usedTextures.Count); + } + + public TextureDescriptor[] GetImageDescriptors() + { + return GetDescriptors(_usedImages, _usedImages.Count); + } + + private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary 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) { 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), }); - 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) @@ -240,7 +510,7 @@ namespace Ryujinx.Graphics.Shader.Translation 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) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 42e3ecee..0fa75203 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (texOp.Inst == Instruction.TextureSample) { - node = InsertCoordNormalization(node, config); + node = InsertCoordNormalization(hfm, node, config); node = InsertCoordGatherBias(node, config); node = InsertConstOffsets(node, config); @@ -285,8 +285,8 @@ namespace Ryujinx.Graphics.Shader.Translation { int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale); int samplerIndex = isImage - ? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp) - : config.FindTextureDescriptorIndex(texOp); + ? config.ResourceManager.GetTextureDescriptors().Length + config.ResourceManager.FindImageDescriptorIndex(texOp.Binding) + : config.ResourceManager.FindTextureDescriptorIndex(texOp.Binding); for (int index = 0; index < coordsCount; index++) { @@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Shader.Translation TypeSupportsScale(texOp.Type)) { 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--) { @@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Shader.Translation return (type & SamplerType.Mask) == SamplerType.Texture2D; } - private static LinkedListNode InsertCoordNormalization(LinkedListNode node, ShaderConfig config) + private static LinkedListNode InsertCoordNormalization(HelperFunctionManager hfm, LinkedListNode node, ShaderConfig config) { // 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, @@ -378,9 +378,17 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation texOp = (TextureOperation)node.Value; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + + if (isBindless) + { + return node; + } + 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) { @@ -411,18 +419,17 @@ namespace Ryujinx.Graphics.Shader.Translation texSizeSources = new Operand[] { Const(0) }; } - node.List.AddBefore(node, new TextureOperation( + LinkedListNode textureSizeNode = node.List.AddBefore(node, new TextureOperation( Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, index, new[] { coordSize }, 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); @@ -431,6 +438,8 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize))); texOp.SetSource(coordsIndex + index, coordNormalized); + + InsertTextureSizeUnscale(hfm, textureSizeNode, config); } return node; @@ -491,14 +500,11 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, index, new[] { coordSize }, texSizeSources)); - config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle); - node.List.AddBefore(node, new Operation( Instruction.FP32 | Instruction.Multiply, scaledSize, @@ -686,8 +692,6 @@ namespace Ryujinx.Graphics.Shader.Translation 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 intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)]; @@ -712,8 +716,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, 1, new[] { dests[destIndex++] }, newSources); @@ -744,8 +747,6 @@ namespace Ryujinx.Graphics.Shader.Translation 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 intOffset = offsets[index]; @@ -771,8 +772,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, componentIndex, dests, sources); @@ -806,8 +806,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, 0, new[] { lod }, lodSources)); @@ -832,8 +831,7 @@ namespace Ryujinx.Graphics.Shader.Translation texOp.Type, texOp.Format, texOp.Flags, - texOp.CbufSlot, - texOp.Handle, + texOp.Binding, index, new[] { texSizes[index] }, texSizeSources)); @@ -853,7 +851,9 @@ namespace Ryujinx.Graphics.Shader.Translation 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 { diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index e93a709c..5741d028 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -2,16 +2,12 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; namespace Ryujinx.Graphics.Shader.Translation { class ShaderConfig { - // TODO: Non-hardcoded array size. - public const int SamplerArraySize = 4; - private const int ThreadsPerWarp = 32; public ShaderStage Stage { get; } @@ -110,20 +106,6 @@ namespace Ryujinx.Graphics.Shader.Translation public UInt128 NextInputAttributesComponents { 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 _usedTextures; - private readonly Dictionary _usedImages; - private TextureDescriptor[] _cachedTextureDescriptors; - private TextureDescriptor[] _cachedImageDescriptors; - public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options, int localMemorySize) { Stage = stage; @@ -141,9 +123,6 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributesPerPatch = new HashSet(); UsedOutputAttributesPerPatch = new HashSet(); - _usedTextures = new Dictionary(); - _usedImages = new Dictionary(); - ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties()); 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); - Properties.AddStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); + Properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer); StructureType tfeDataStruct = new(new StructureField[] { @@ -167,7 +146,7 @@ namespace Ryujinx.Graphics.Shader.Translation { int binding = Constants.TfeBufferBaseBinding + i; 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; 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) @@ -642,196 +605,13 @@ namespace Ryujinx.Graphics.Shader.Translation 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 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 dict, Func 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) { return new ShaderProgramInfo( ResourceManager.GetConstantBufferDescriptors(), ResourceManager.GetStorageBufferDescriptors(), - GetTextureDescriptors(), - GetImageDescriptors(), + ResourceManager.GetTextureDescriptors(), + ResourceManager.GetImageDescriptors(), identification, GpLayerInputAttribute, Stage,