diff options
author | gdkchan <gab.dark.100@gmail.com> | 2023-07-03 14:29:27 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-03 14:29:27 -0300 |
commit | 1c7a90ef359d9974e5bd257c4d8e9bf526a6966c (patch) | |
tree | 3ab1644927819b90b0aef78ed6749c6434150490 /src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs | |
parent | 3b46bb73f781a011705ecbc8a1d3207dfb145829 (diff) |
Stop identifying shader textures with handle and cbuf, use binding instead (#5266)1.1.952
* Stop identifying shader textures with handle and cbuf, use binding instead
* Remove now unused code
* Consider image operations as having accurate type information too
I don't know why that was not the case before
* Fix missing unscale on InsertCoordNormalization, stop calling SetUsageFlagsForTextureQuery when not needed
* Shader cache version bump
* Change get texture methods to return descriptors created from ResourceManager state
This is required to ensure that reserved textures and images will not be bound as a guest texture/image
* Fix BindlessElimination.SetHandle inserting coords at the wrong place
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs | 286 |
1 files changed, 278 insertions, 8 deletions
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) |