aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Shader
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-04-07 18:25:55 -0300
committerGitHub <noreply@github.com>2024-04-07 18:25:55 -0300
commit3e6e0e4afaa3c3ffb118cb17b61feb16966a7eeb (patch)
treea4652499c089b0853e39c382cad82a9db4d6ad08 /src/Ryujinx.Graphics.Gpu/Shader
parent808803d97a0c06809bf000687c252f960048fcf0 (diff)
Add support for large sampler arrays on Vulkan (#6489)
* Add support for large sampler arrays on Vulkan * Shader cache version bump * Format whitespace * Move DescriptorSetManager to PipelineLayoutCacheEntry to allow different pool sizes per layout * Handle array textures with different types on the same buffer * Somewhat better caching system * Avoid useless buffer data modification checks * Move redundant bindings update checking to the backend * Fix an issue where texture arrays would get the same bindings across stages on Vulkan * Backport some fixes from part 2 * Fix typo * PR feedback * Format whitespace * Add some missing XML docs
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Shader')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs17
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs29
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs2
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs6
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs29
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs77
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs35
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs75
8 files changed, 226 insertions, 44 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
index 4e1cb4e1..6e36753e 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
@@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public BufferDescriptor[][] ConstantBufferBindings { get; }
public BufferDescriptor[][] StorageBufferBindings { get; }
+ public int[] TextureCounts { get; }
+
public int MaxTextureBinding { get; }
public int MaxImageBinding { get; }
@@ -34,6 +36,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
ConstantBufferBindings = new BufferDescriptor[stageCount][];
StorageBufferBindings = new BufferDescriptor[stageCount][];
+ TextureCounts = new int[stageCount];
+
int maxTextureBinding = -1;
int maxImageBinding = -1;
int offset = isCompute ? 0 : 1;
@@ -59,13 +63,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
var result = new TextureBindingInfo(
target,
descriptor.Binding,
+ descriptor.ArrayLength,
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Flags);
- if (descriptor.Binding > maxTextureBinding)
+ if (descriptor.ArrayLength <= 1)
{
- maxTextureBinding = descriptor.Binding;
+ if (descriptor.Binding > maxTextureBinding)
+ {
+ maxTextureBinding = descriptor.Binding;
+ }
+
+ TextureCounts[i]++;
}
return result;
@@ -80,11 +90,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
target,
format,
descriptor.Binding,
+ descriptor.ArrayLength,
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Flags);
- if (descriptor.Binding > maxImageBinding)
+ if (descriptor.ArrayLength <= 1 && descriptor.Binding > maxImageBinding)
{
maxImageBinding = descriptor.Binding;
}
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
index de6432bc..681838a9 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <param name="cb1Data">The constant buffer 1 data of the shader</param>
/// <param name="oldSpecState">Shader specialization state of the cached shader</param>
/// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
+ /// <param name="counts">Resource counts shared across all shader stages</param>
/// <param name="stageIndex">Shader stage index</param>
public DiskCacheGpuAccessor(
GpuContext context,
@@ -109,18 +110,32 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
/// <inheritdoc/>
- public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
+ public SamplerType QuerySamplerType(int handle, int cbufSlot)
{
- _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot);
- (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot);
- return ConvertToTextureFormat(format, formatSrgb);
+ _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
+ return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType();
}
/// <inheritdoc/>
- public SamplerType QuerySamplerType(int handle, int cbufSlot)
+ public int QueryTextureArrayLengthFromBuffer(int slot)
{
- _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
- return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType();
+ if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot))
+ {
+ throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength);
+ }
+
+ int arrayLength = _oldSpecState.GetTextureArrayFromBufferLength(_stageIndex, 0, slot);
+ _newSpecState.RegisterTextureArrayLengthFromBuffer(_stageIndex, 0, slot, arrayLength);
+
+ return arrayLength;
+ }
+
+ /// <inheritdoc/>
+ public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
+ {
+ _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot);
+ (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot);
+ return ConvertToTextureFormat(format, formatSrgb);
}
/// <inheritdoc/>
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index 5036186b..b6a277a2 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 = 6462;
+ private const uint CodeGenVersion = 6489;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs
index ba23f70e..d5abb9e5 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs
@@ -21,6 +21,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
InvalidCb1DataLength,
/// <summary>
+ /// The cache is missing the length of a texture array used by the shader.
+ /// </summary>
+ MissingTextureArrayLength,
+
+ /// <summary>
/// The cache is missing the descriptor of a texture used by the shader.
/// </summary>
MissingTextureDescriptor,
@@ -60,6 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
DiskCacheLoadResult.Success => "No error.",
DiskCacheLoadResult.NoAccess => "Could not access the cache file.",
DiskCacheLoadResult.InvalidCb1DataLength => "Constant buffer 1 data length is too low.",
+ DiskCacheLoadResult.MissingTextureArrayLength => "Texture array length missing from the cache file.",
DiskCacheLoadResult.MissingTextureDescriptor => "Texture descriptor missing from the cache file.",
DiskCacheLoadResult.FileCorruptedGeneric => "The cache file is corrupted.",
DiskCacheLoadResult.FileCorruptedInvalidMagic => "Magic check failed, the cache file is corrupted.",
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 95763f31..1d22ab93 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -72,6 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
public ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize)
{
int size = Math.Max(minimumSize, 0x1000 - (int)(address & 0xfff));
+
return MemoryMarshal.Cast<byte, ulong>(_channel.MemoryManager.GetSpan(address, size));
}
@@ -119,6 +120,27 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
}
+ /// <inheritdoc/>
+ public SamplerType QuerySamplerType(int handle, int cbufSlot)
+ {
+ _state.SpecializationState?.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
+ return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget().ConvertSamplerType();
+ }
+
+ /// <inheritdoc/>
+ public int QueryTextureArrayLengthFromBuffer(int slot)
+ {
+ int size = _compute
+ ? _channel.BufferManager.GetComputeUniformBufferSize(slot)
+ : _channel.BufferManager.GetGraphicsUniformBufferSize(_stageIndex, slot);
+
+ int arrayLength = size / Constants.TextureHandleSizeInBytes;
+
+ _state.SpecializationState?.RegisterTextureArrayLengthFromBuffer(_stageIndex, 0, slot, arrayLength);
+
+ return arrayLength;
+ }
+
//// <inheritdoc/>
public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
{
@@ -128,13 +150,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <inheritdoc/>
- public SamplerType QuerySamplerType(int handle, int cbufSlot)
- {
- _state.SpecializationState?.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
- return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget().ConvertSamplerType();
- }
-
- /// <inheritdoc/>
public bool QueryTextureCoordNormalized(int handle, int cbufSlot)
{
_state.SpecializationState?.RecordTextureCoordNormalized(_stageIndex, handle, cbufSlot);
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index a5b31363..06e5edf1 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -20,6 +20,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
private int _reservedTextures;
private int _reservedImages;
+ private int _staticTexturesCount;
+ private int _staticImagesCount;
+
/// <summary>
/// Creates a new GPU accessor.
/// </summary>
@@ -48,7 +51,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_reservedImages = rrc.ReservedImages;
}
- public int QueryBindingConstantBuffer(int index)
+ public int CreateConstantBufferBinding(int index)
{
int binding;
@@ -64,62 +67,84 @@ namespace Ryujinx.Graphics.Gpu.Shader
return binding + _reservedConstantBuffers;
}
- public int QueryBindingStorageBuffer(int index)
+ public int CreateImageBinding(int count, bool isBuffer)
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
- binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
+ if (count == 1)
+ {
+ int index = _staticImagesCount++;
+
+ if (isBuffer)
+ {
+ index += (int)_context.Capabilities.MaximumImagesPerStage;
+ }
+
+ binding = GetBindingFromIndex(index, _context.Capabilities.MaximumImagesPerStage * 2, "Image");
+ }
+ else
+ {
+ binding = (int)GetDynamicBaseIndexDual(_context.Capabilities.MaximumImagesPerStage) + _resourceCounts.ImagesCount++;
+ }
}
else
{
- binding = _resourceCounts.StorageBuffersCount++;
+ binding = _resourceCounts.ImagesCount;
+
+ _resourceCounts.ImagesCount += count;
}
- return binding + _reservedStorageBuffers;
+ return binding + _reservedImages;
}
- public int QueryBindingTexture(int index, bool isBuffer)
+ public int CreateStorageBufferBinding(int index)
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
- if (isBuffer)
- {
- index += (int)_context.Capabilities.MaximumTexturesPerStage;
- }
-
- binding = GetBindingFromIndex(index, _context.Capabilities.MaximumTexturesPerStage * 2, "Texture");
+ binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
}
else
{
- binding = _resourceCounts.TexturesCount++;
+ binding = _resourceCounts.StorageBuffersCount++;
}
- return binding + _reservedTextures;
+ return binding + _reservedStorageBuffers;
}
- public int QueryBindingImage(int index, bool isBuffer)
+ public int CreateTextureBinding(int count, bool isBuffer)
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
- if (isBuffer)
+ if (count == 1)
{
- index += (int)_context.Capabilities.MaximumImagesPerStage;
- }
+ int index = _staticTexturesCount++;
+
+ if (isBuffer)
+ {
+ index += (int)_context.Capabilities.MaximumTexturesPerStage;
+ }
- binding = GetBindingFromIndex(index, _context.Capabilities.MaximumImagesPerStage * 2, "Image");
+ binding = GetBindingFromIndex(index, _context.Capabilities.MaximumTexturesPerStage * 2, "Texture");
+ }
+ else
+ {
+ binding = (int)GetDynamicBaseIndexDual(_context.Capabilities.MaximumTexturesPerStage) + _resourceCounts.TexturesCount++;
+ }
}
else
{
- binding = _resourceCounts.ImagesCount++;
+ binding = _resourceCounts.TexturesCount;
+
+ _resourceCounts.TexturesCount += count;
}
- return binding + _reservedImages;
+ return binding + _reservedTextures;
}
private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName)
@@ -148,6 +173,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
};
}
+ private static uint GetDynamicBaseIndexDual(uint maxPerStage)
+ {
+ return GetDynamicBaseIndex(maxPerStage) * 2;
+ }
+
+ private static uint GetDynamicBaseIndex(uint maxPerStage)
+ {
+ return maxPerStage * Constants.ShaderStages;
+ }
+
public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision;
public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
index c2258026..ea8f164f 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
@@ -132,6 +132,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
+ AddArrayDescriptors(info.Textures, stages, TextureSetIndex, isImage: false);
+ AddArrayDescriptors(info.Images, stages, TextureSetIndex, isImage: true);
+
AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false);
AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true);
AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
@@ -170,6 +173,30 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Adds all array descriptors (those with an array length greater than one).
+ /// </summary>
+ /// <param name="textures">Textures to be added</param>
+ /// <param name="stages">Stages where the textures are used</param>
+ /// <param name="setIndex">Descriptor set index where the textures will be bound</param>
+ /// <param name="isImage">True for images, false for textures</param>
+ private void AddArrayDescriptors(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage)
+ {
+ foreach (TextureDescriptor texture in textures)
+ {
+ if (texture.ArrayLength > 1)
+ {
+ bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
+
+ ResourceType type = isBuffer
+ ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
+ : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
+
+ _resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages));
+ }
+ }
+ }
+
+ /// <summary>
/// Adds buffer usage information to the list of usages.
/// </summary>
/// <param name="stages">Shader stages where the resource is used</param>
@@ -181,7 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
for (int index = 0; index < count; index++)
{
- _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, type, stages));
+ _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages));
}
}
@@ -198,6 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
_resourceUsages[setIndex].Add(new ResourceUsage(
buffer.Binding,
+ 1,
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
stages));
}
@@ -220,10 +248,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
: (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
- _resourceUsages[setIndex].Add(new ResourceUsage(
- texture.Binding,
- type,
- stages));
+ _resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
}
}
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index 1477b738..c90a0b8f 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -30,6 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
PrimitiveTopology = 1 << 1,
TransformFeedback = 1 << 3,
+ TextureArrayFromBuffer = 1 << 4,
}
private QueriedStateFlags _queriedState;
@@ -153,6 +154,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
+ private readonly Dictionary<TextureKey, int> _textureArraySpecialization;
private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures;
private Box<TextureSpecializationState>[][] _textureByBinding;
private Box<TextureSpecializationState>[][] _imageByBinding;
@@ -163,6 +165,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private ShaderSpecializationState()
{
_textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>();
+ _textureArraySpecialization = new Dictionary<TextureKey, int>();
}
/// <summary>
@@ -324,6 +327,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Indicates that the coordinate normalization state of a given texture was used during the shader translation process.
+ /// </summary>
+ /// <param name="stageIndex">Shader stage where the texture is used</param>
+ /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
+ /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
+ /// <param name="length">Number of elements in the texture array</param>
+ public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length)
+ {
+ _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length;
+ _queriedState |= QueriedStateFlags.TextureArrayFromBuffer;
+ }
+
+ /// <summary>
/// Indicates that the format of a given texture was used during the shader translation process.
/// </summary>
/// <param name="stageIndex">Shader stage where the texture is used</param>
@@ -380,6 +396,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Checks if a given texture array (from constant buffer) was registerd on this specialization state.
+ /// </summary>
+ /// <param name="stageIndex">Shader stage where the texture is used</param>
+ /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
+ /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
+ public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot)
+ {
+ return _textureArraySpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot));
+ }
+
+ /// <summary>
/// Gets the recorded format of a given texture.
/// </summary>
/// <param name="stageIndex">Shader stage where the texture is used</param>
@@ -414,6 +441,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Gets the recorded length of a given texture array (from constant buffer).
+ /// </summary>
+ /// <param name="stageIndex">Shader stage where the texture is used</param>
+ /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
+ /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
+ public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot)
+ {
+ return _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)];
+ }
+
+ /// <summary>
/// Gets texture specialization state for a given texture, or create a new one if not present.
/// </summary>
/// <param name="stageIndex">Shader stage where the texture is used</param>
@@ -548,6 +586,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return Matches(channel, ref poolState, checkTextures, isCompute: false);
}
+ /// <summary>
+ /// Converts special vertex attribute groups to their generic equivalents, for comparison purposes.
+ /// </summary>
+ /// <param name="channel">GPU channel</param>
+ /// <param name="type">Vertex attribute type</param>
+ /// <returns>Filtered attribute</returns>
private static AttributeType FilterAttributeType(GpuChannel channel, AttributeType type)
{
type &= ~(AttributeType.Packed | AttributeType.PackedRgb10A2Signed);
@@ -838,6 +882,22 @@ namespace Ryujinx.Graphics.Gpu.Shader
specState._textureSpecialization[textureKey] = textureState;
}
+ if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
+ {
+ dataReader.Read(ref count);
+
+ for (int index = 0; index < count; index++)
+ {
+ TextureKey textureKey = default;
+ int length = 0;
+
+ dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
+ dataReader.Read(ref length);
+
+ specState._textureArraySpecialization[textureKey] = length;
+ }
+ }
+
return specState;
}
@@ -902,6 +962,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic);
dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
}
+
+ if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
+ {
+ count = (ushort)_textureArraySpecialization.Count;
+ dataWriter.Write(ref count);
+
+ foreach (var kv in _textureArraySpecialization)
+ {
+ var textureKey = kv.Key;
+ var length = kv.Value;
+
+ dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic);
+ dataWriter.Write(ref length);
+ }
+ }
}
}
}