| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- using Ryujinx.Common;
- using Ryujinx.Graphics.Shader.IntermediateRepresentation;
- using Ryujinx.Graphics.Shader.StructuredIr;
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- namespace Ryujinx.Graphics.Shader.Translation
- {
- class ResourceManager
- {
- // Those values are used if the shader as local or shared memory access,
- // but for some reason the supplied size was 0.
- private const int DefaultLocalMemorySize = 128;
- private const int DefaultSharedMemorySize = 4096;
- // 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;
- private readonly int[] _sbSlotToBindingMap;
- private uint _sbSlotWritten;
- private readonly Dictionary<int, int> _sbSlots;
- private readonly Dictionary<int, int> _sbSlotsReverse;
- 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; }
- public ShaderProperties Properties { get; }
- public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
- {
- _gpuAccessor = gpuAccessor;
- Properties = properties;
- _stage = stage;
- _stagePrefix = GetShaderStagePrefix(stage);
- _cbSlotToBindingMap = new int[18];
- _sbSlotToBindingMap = new int[16];
- _cbSlotToBindingMap.AsSpan().Fill(-1);
- _sbSlotToBindingMap.AsSpan().Fill(-1);
- _sbSlots = new Dictionary<int, int>();
- _sbSlotsReverse = new Dictionary<int, int>();
- _usedConstantBufferBindings = new HashSet<int>();
- _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;
- }
- public void SetCurrentLocalMemory(int size, bool isUsed)
- {
- if (isUsed)
- {
- if (size <= 0)
- {
- size = DefaultLocalMemorySize;
- }
- var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint)));
- LocalMemoryId = Properties.AddLocalMemory(lmem);
- }
- else
- {
- LocalMemoryId = -1;
- }
- }
- public void SetCurrentSharedMemory(int size, bool isUsed)
- {
- if (isUsed)
- {
- if (size <= 0)
- {
- size = DefaultSharedMemorySize;
- }
- var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint)));
- SharedMemoryId = Properties.AddSharedMemory(smem);
- }
- else
- {
- SharedMemoryId = -1;
- }
- }
- public int GetConstantBufferBinding(int slot)
- {
- int binding = _cbSlotToBindingMap[slot];
- if (binding < 0)
- {
- binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
- _cbSlotToBindingMap[slot] = binding;
- string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
- AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
- }
- return binding;
- }
- public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding)
- {
- if (!TryGetSbSlot((byte)sbCbSlot, (ushort)sbCbOffset, out int slot))
- {
- binding = 0;
- return false;
- }
- binding = _sbSlotToBindingMap[slot];
- if (binding < 0)
- {
- binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
- _sbSlotToBindingMap[slot] = binding;
- string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
- AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
- }
- if (write)
- {
- _sbSlotWritten |= 1u << slot;
- }
- return true;
- }
- private bool TryGetSbSlot(byte sbCbSlot, ushort sbCbOffset, out int slot)
- {
- int key = PackSbCbInfo(sbCbSlot, sbCbOffset);
- if (!_sbSlots.TryGetValue(key, out slot))
- {
- slot = _sbSlots.Count;
- if (slot >= _sbSlotToBindingMap.Length)
- {
- return false;
- }
- _sbSlots.Add(key, slot);
- _sbSlotsReverse.Add(slot, key);
- }
- return true;
- }
- public bool TryGetConstantBufferSlot(int binding, out int slot)
- {
- for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
- {
- if (_cbSlotToBindingMap[slot] == binding)
- {
- return true;
- }
- }
- slot = 0;
- 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);
- }
- public BufferDescriptor[] GetConstantBufferDescriptors()
- {
- var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count];
- int descriptorIndex = 0;
- for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
- {
- int binding = _cbSlotToBindingMap[slot];
- if (binding >= 0 && _usedConstantBufferBindings.Contains(binding))
- {
- descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot);
- }
- }
- if (descriptors.Length != descriptorIndex)
- {
- Array.Resize(ref descriptors, descriptorIndex);
- }
- return descriptors;
- }
- public BufferDescriptor[] GetStorageBufferDescriptors()
- {
- var descriptors = new BufferDescriptor[_sbSlots.Count];
- int descriptorIndex = 0;
- foreach ((int key, int slot) in _sbSlots)
- {
- int binding = _sbSlotToBindingMap[slot];
- if (binding >= 0)
- {
- (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key);
- BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None;
- descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset, flags);
- }
- }
- if (descriptors.Length != descriptorIndex)
- {
- Array.Resize(ref descriptors, descriptorIndex);
- }
- 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[]
- {
- new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16),
- });
- Properties.AddOrUpdateConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
- }
- private void AddNewStorageBuffer(int binding, string name)
- {
- StructureType type = new(new[]
- {
- new StructureField(AggregateType.Array | AggregateType.U32, "data", 0),
- });
- Properties.AddOrUpdateStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type));
- }
- public static string GetShaderStagePrefix(ShaderStage stage)
- {
- uint index = (uint)stage;
- return index >= _stagePrefixes.Length ? "invalid" : _stagePrefixes[index];
- }
- private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset)
- {
- return sbCbOffset | (sbCbSlot << 16);
- }
- private static (int, int) UnpackSbCbInfo(int key)
- {
- return ((byte)(key >> 16), (ushort)key);
- }
- }
- }
|