mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-19 03:16:29 +02:00
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
This commit is contained in:
parent
808803d97a
commit
3e6e0e4afa
83 changed files with 3263 additions and 955 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -108,6 +109,27 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SamplerType QuerySamplerType(int handle, int cbufSlot)
|
||||
{
|
||||
_newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
|
||||
return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryTextureArrayLengthFromBuffer(int slot)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -116,13 +138,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
return ConvertToTextureFormat(format, formatSrgb);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SamplerType QuerySamplerType(int handle, int cbufSlot)
|
||||
{
|
||||
_newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
|
||||
return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryTextureCoordNormalized(int handle, int cbufSlot)
|
||||
{
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
/// </summary>
|
||||
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>
|
||||
|
@ -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.",
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -127,13 +149,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return ConvertToTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb());
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
|
|
|
@ -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,7 +67,39 @@ 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)
|
||||
{
|
||||
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.ImagesCount;
|
||||
|
||||
_resourceCounts.ImagesCount += count;
|
||||
}
|
||||
|
||||
return binding + _reservedImages;
|
||||
}
|
||||
|
||||
public int CreateStorageBufferBinding(int index)
|
||||
{
|
||||
int binding;
|
||||
|
||||
|
@ -80,48 +115,38 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return binding + _reservedStorageBuffers;
|
||||
}
|
||||
|
||||
public int QueryBindingTexture(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.MaximumTexturesPerStage;
|
||||
}
|
||||
int index = _staticTexturesCount++;
|
||||
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumTexturesPerStage * 2, "Texture");
|
||||
if (isBuffer)
|
||||
{
|
||||
index += (int)_context.Capabilities.MaximumTexturesPerStage;
|
||||
}
|
||||
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumTexturesPerStage * 2, "Texture");
|
||||
}
|
||||
else
|
||||
{
|
||||
binding = (int)GetDynamicBaseIndexDual(_context.Capabilities.MaximumTexturesPerStage) + _resourceCounts.TexturesCount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
binding = _resourceCounts.TexturesCount++;
|
||||
binding = _resourceCounts.TexturesCount;
|
||||
|
||||
_resourceCounts.TexturesCount += count;
|
||||
}
|
||||
|
||||
return binding + _reservedTextures;
|
||||
}
|
||||
|
||||
public int QueryBindingImage(int index, bool isBuffer)
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
if (isBuffer)
|
||||
{
|
||||
index += (int)_context.Capabilities.MaximumImagesPerStage;
|
||||
}
|
||||
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumImagesPerStage * 2, "Image");
|
||||
}
|
||||
else
|
||||
{
|
||||
binding = _resourceCounts.ImagesCount++;
|
||||
}
|
||||
|
||||
return binding + _reservedImages;
|
||||
}
|
||||
|
||||
private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName)
|
||||
{
|
||||
if ((uint)index >= maxPerStage)
|
||||
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -169,6 +172,30 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
AddDescriptor(stages, type2, setIndex, binding + count, count);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
@ -323,6 +326,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
state.Value.CoordNormalized = coordNormalized;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
@ -379,6 +395,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return GetTextureSpecState(stageIndex, handle, cbufSlot) != null;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
@ -413,6 +440,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue