diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Translation')
7 files changed, 412 insertions, 296 deletions
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<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 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<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; 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<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) { 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<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. // 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<INode> 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<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) { Stage = stage; @@ -141,9 +123,6 @@ namespace Ryujinx.Graphics.Shader.Translation UsedInputAttributesPerPatch = new HashSet<int>(); UsedOutputAttributesPerPatch = new HashSet<int>(); - _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); - _usedImages = new Dictionary<TextureInfo, TextureMeta>(); - 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<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) { return new ShaderProgramInfo( ResourceManager.GetConstantBufferDescriptors(), ResourceManager.GetStorageBufferDescriptors(), - GetTextureDescriptors(), - GetImageDescriptors(), + ResourceManager.GetTextureDescriptors(), + ResourceManager.GetImageDescriptors(), identification, GpLayerInputAttribute, Stage, |
