aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-04-22 15:05:55 -0300
committerGitHub <noreply@github.com>2024-04-22 15:05:55 -0300
commitc6f8bfed904e30f7c5d890a2f0ef531eb9e298e5 (patch)
treee1c048d390867e8c9403904498184e3a64277e49
parent9b94662b4bb2ebf846e1baf45ba8097fcd7da684 (diff)
Add support for bindless textures from shader input (vertex buffer) on Vulkan (#6577)1.1.1291
* Add support for bindless textures from shader input (vertex buffer) * Shader cache version bump * Format whitespace * Remove cache entries on pool removal, disable for OpenGL * PR feedback
-rw-r--r--src/Ryujinx.Graphics.GAL/Capabilities.cs3
-rw-r--r--src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs6
-rw-r--r--src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs13
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs7
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs16
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs616
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs16
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs8
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs5
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs35
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs2
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs18
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs2
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs8
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs7
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs8
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs38
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs92
-rw-r--r--src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs1
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs3
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs19
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs54
-rw-r--r--src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs86
-rw-r--r--src/Ryujinx.Graphics.Shader/IGpuAccessor.cs35
-rw-r--r--src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs5
-rw-r--r--src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs9
-rw-r--r--src/Ryujinx.Graphics.Shader/SamplerType.cs26
-rw-r--r--src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs5
-rw-r--r--src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs2
-rw-r--r--src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs18
-rw-r--r--src/Ryujinx.Graphics.Shader/TextureDescriptor.cs4
-rw-r--r--src/Ryujinx.Graphics.Shader/TextureHandle.cs1
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs86
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs2
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs32
-rw-r--r--src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs6
-rw-r--r--src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs87
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs1
-rw-r--r--src/Ryujinx.ShaderTools/Program.cs10
39 files changed, 1086 insertions, 306 deletions
diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs
index dc927eaba..70736fbd6 100644
--- a/src/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.GAL
36 public readonly bool SupportsMismatchingViewFormat; 36 public readonly bool SupportsMismatchingViewFormat;
37 public readonly bool SupportsCubemapView; 37 public readonly bool SupportsCubemapView;
38 public readonly bool SupportsNonConstantTextureOffset; 38 public readonly bool SupportsNonConstantTextureOffset;
39 public readonly bool SupportsSeparateSampler;
39 public readonly bool SupportsShaderBallot; 40 public readonly bool SupportsShaderBallot;
40 public readonly bool SupportsShaderBarrierDivergence; 41 public readonly bool SupportsShaderBarrierDivergence;
41 public readonly bool SupportsShaderFloat64; 42 public readonly bool SupportsShaderFloat64;
@@ -92,6 +93,7 @@ namespace Ryujinx.Graphics.GAL
92 bool supportsMismatchingViewFormat, 93 bool supportsMismatchingViewFormat,
93 bool supportsCubemapView, 94 bool supportsCubemapView,
94 bool supportsNonConstantTextureOffset, 95 bool supportsNonConstantTextureOffset,
96 bool supportsSeparateSampler,
95 bool supportsShaderBallot, 97 bool supportsShaderBallot,
96 bool supportsShaderBarrierDivergence, 98 bool supportsShaderBarrierDivergence,
97 bool supportsShaderFloat64, 99 bool supportsShaderFloat64,
@@ -144,6 +146,7 @@ namespace Ryujinx.Graphics.GAL
144 SupportsMismatchingViewFormat = supportsMismatchingViewFormat; 146 SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
145 SupportsCubemapView = supportsCubemapView; 147 SupportsCubemapView = supportsCubemapView;
146 SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; 148 SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
149 SupportsSeparateSampler = supportsSeparateSampler;
147 SupportsShaderBallot = supportsShaderBallot; 150 SupportsShaderBallot = supportsShaderBallot;
148 SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; 151 SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
149 SupportsShaderFloat64 = supportsShaderFloat64; 152 SupportsShaderFloat64 = supportsShaderFloat64;
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
index ccdbe4748..cd8144724 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
@@ -126,6 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
126 ulong samplerPoolGpuVa = ((ulong)_state.State.SetTexSamplerPoolAOffsetUpper << 32) | _state.State.SetTexSamplerPoolB; 126 ulong samplerPoolGpuVa = ((ulong)_state.State.SetTexSamplerPoolAOffsetUpper << 32) | _state.State.SetTexSamplerPoolB;
127 ulong texturePoolGpuVa = ((ulong)_state.State.SetTexHeaderPoolAOffsetUpper << 32) | _state.State.SetTexHeaderPoolB; 127 ulong texturePoolGpuVa = ((ulong)_state.State.SetTexHeaderPoolAOffsetUpper << 32) | _state.State.SetTexHeaderPoolB;
128 128
129 int samplerPoolMaximumId = _state.State.SetTexSamplerPoolCMaximumIndex;
130
129 GpuChannelPoolState poolState = new( 131 GpuChannelPoolState poolState = new(
130 texturePoolGpuVa, 132 texturePoolGpuVa,
131 _state.State.SetTexHeaderPoolCMaximumIndex, 133 _state.State.SetTexHeaderPoolCMaximumIndex,
@@ -139,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
139 sharedMemorySize, 141 sharedMemorySize,
140 _channel.BufferManager.HasUnalignedStorageBuffers); 142 _channel.BufferManager.HasUnalignedStorageBuffers);
141 143
142 CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); 144 CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
143 145
144 _context.Renderer.Pipeline.SetProgram(cs.HostProgram); 146 _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
145 147
@@ -184,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
184 sharedMemorySize, 186 sharedMemorySize,
185 _channel.BufferManager.HasUnalignedStorageBuffers); 187 _channel.BufferManager.HasUnalignedStorageBuffers);
186 188
187 cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); 189 cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
188 190
189 _context.Renderer.Pipeline.SetProgram(cs.HostProgram); 191 _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
190 } 192 }
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index b3eb62185..1dc77b52d 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -1429,7 +1429,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
1429 addressesSpan[index] = baseAddress + shader.Offset; 1429 addressesSpan[index] = baseAddress + shader.Offset;
1430 } 1430 }
1431 1431
1432 CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, ref _currentSpecState.GetPoolState(), ref _currentSpecState.GetGraphicsState(), addresses); 1432 int samplerPoolMaximumId = _state.State.SamplerIndex == SamplerIndex.ViaHeaderIndex
1433 ? _state.State.TexturePoolState.MaximumId
1434 : _state.State.SamplerPoolState.MaximumId;
1435
1436 CachedShaderProgram gs = shaderCache.GetGraphicsShader(
1437 ref _state.State,
1438 ref _pipeline,
1439 _channel,
1440 samplerPoolMaximumId,
1441 ref _currentSpecState.GetPoolState(),
1442 ref _currentSpecState.GetGraphicsState(),
1443 addresses);
1433 1444
1434 // Consume the modified flag for spec state so that it isn't checked again. 1445 // Consume the modified flag for spec state so that it isn't checked again.
1435 _currentSpecState.SetShader(gs); 1446 _currentSpecState.SetShader(gs);
diff --git a/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
index d9881f897..50872ab63 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
@@ -62,8 +62,9 @@ namespace Ryujinx.Graphics.Gpu.Image
62 /// <param name="channel">GPU channel that the texture pool cache belongs to</param> 62 /// <param name="channel">GPU channel that the texture pool cache belongs to</param>
63 /// <param name="address">Start address of the texture pool</param> 63 /// <param name="address">Start address of the texture pool</param>
64 /// <param name="maximumId">Maximum ID of the texture pool</param> 64 /// <param name="maximumId">Maximum ID of the texture pool</param>
65 /// <param name="bindingsArrayCache">Cache of texture array bindings</param>
65 /// <returns>The found or newly created texture pool</returns> 66 /// <returns>The found or newly created texture pool</returns>
66 public T FindOrCreate(GpuChannel channel, ulong address, int maximumId) 67 public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
67 { 68 {
68 // Remove old entries from the cache, if possible. 69 // Remove old entries from the cache, if possible.
69 while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval) 70 while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
@@ -73,6 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Image
73 _pools.RemoveFirst(); 74 _pools.RemoveFirst();
74 oldestPool.Dispose(); 75 oldestPool.Dispose();
75 oldestPool.CacheNode = null; 76 oldestPool.CacheNode = null;
77 bindingsArrayCache.RemoveAllWithPool(oldestPool);
76 } 78 }
77 79
78 T pool; 80 T pool;
@@ -87,8 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Image
87 if (pool.CacheNode != _pools.Last) 89 if (pool.CacheNode != _pools.Last)
88 { 90 {
89 _pools.Remove(pool.CacheNode); 91 _pools.Remove(pool.CacheNode);
90 92 _pools.AddLast(pool.CacheNode);
91 pool.CacheNode = _pools.AddLast(pool);
92 } 93 }
93 94
94 pool.CacheTimestamp = _currentTimestamp; 95 pool.CacheTimestamp = _currentTimestamp;
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
index 12a457dbc..ba895c60a 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
@@ -45,6 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
45 public TextureUsageFlags Flags { get; } 45 public TextureUsageFlags Flags { get; }
46 46
47 /// <summary> 47 /// <summary>
48 /// Indicates that the binding is for a sampler.
49 /// </summary>
50 public bool IsSamplerOnly { get; }
51
52 /// <summary>
48 /// Constructs the texture binding information structure. 53 /// Constructs the texture binding information structure.
49 /// </summary> 54 /// </summary>
50 /// <param name="target">The shader sampler target type</param> 55 /// <param name="target">The shader sampler target type</param>
@@ -74,8 +79,17 @@ namespace Ryujinx.Graphics.Gpu.Image
74 /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param> 79 /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
75 /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param> 80 /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
76 /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param> 81 /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
77 public TextureBindingInfo(Target target, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags) : this(target, (Format)0, binding, arrayLength, cbufSlot, handle, flags) 82 /// <param name="isSamplerOnly">Indicates that the binding is for a sampler</param>
83 public TextureBindingInfo(
84 Target target,
85 int binding,
86 int arrayLength,
87 int cbufSlot,
88 int handle,
89 TextureUsageFlags flags,
90 bool isSamplerOnly) : this(target, 0, binding, arrayLength, cbufSlot, handle, flags)
78 { 91 {
92 IsSamplerOnly = isSamplerOnly;
79 } 93 }
80 } 94 }
81} 95}
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
index 4645317c4..7e486e0a8 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
@@ -21,12 +21,98 @@ namespace Ryujinx.Graphics.Gpu.Image
21 21
22 private readonly GpuContext _context; 22 private readonly GpuContext _context;
23 private readonly GpuChannel _channel; 23 private readonly GpuChannel _channel;
24 private readonly bool _isCompute;
25 24
26 /// <summary> 25 /// <summary>
27 /// Array cache entry key. 26 /// Array cache entry key.
28 /// </summary> 27 /// </summary>
29 private readonly struct CacheEntryKey : IEquatable<CacheEntryKey> 28 private readonly struct CacheEntryFromPoolKey : IEquatable<CacheEntryFromPoolKey>
29 {
30 /// <summary>
31 /// Whether the entry is for an image.
32 /// </summary>
33 public readonly bool IsImage;
34
35 /// <summary>
36 /// Whether the entry is for a sampler.
37 /// </summary>
38 public readonly bool IsSampler;
39
40 /// <summary>
41 /// Texture or image target type.
42 /// </summary>
43 public readonly Target Target;
44
45 /// <summary>
46 /// Number of entries of the array.
47 /// </summary>
48 public readonly int ArrayLength;
49
50 private readonly TexturePool _texturePool;
51 private readonly SamplerPool _samplerPool;
52
53 /// <summary>
54 /// Creates a new array cache entry.
55 /// </summary>
56 /// <param name="isImage">Whether the entry is for an image</param>
57 /// <param name="bindingInfo">Binding information for the array</param>
58 /// <param name="texturePool">Texture pool where the array textures are located</param>
59 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
60 public CacheEntryFromPoolKey(bool isImage, TextureBindingInfo bindingInfo, TexturePool texturePool, SamplerPool samplerPool)
61 {
62 IsImage = isImage;
63 IsSampler = bindingInfo.IsSamplerOnly;
64 Target = bindingInfo.Target;
65 ArrayLength = bindingInfo.ArrayLength;
66
67 _texturePool = texturePool;
68 _samplerPool = samplerPool;
69 }
70
71 /// <summary>
72 /// Checks if the pool matches the cached pool.
73 /// </summary>
74 /// <param name="texturePool">Texture or sampler pool instance</param>
75 /// <returns>True if the pool matches, false otherwise</returns>
76 public bool MatchesPool<T>(IPool<T> pool)
77 {
78 return _texturePool == pool || _samplerPool == pool;
79 }
80
81 /// <summary>
82 /// Checks if the texture and sampler pools matches the cached pools.
83 /// </summary>
84 /// <param name="texturePool">Texture pool instance</param>
85 /// <param name="samplerPool">Sampler pool instance</param>
86 /// <returns>True if the pools match, false otherwise</returns>
87 private bool MatchesPools(TexturePool texturePool, SamplerPool samplerPool)
88 {
89 return _texturePool == texturePool && _samplerPool == samplerPool;
90 }
91
92 public bool Equals(CacheEntryFromPoolKey other)
93 {
94 return IsImage == other.IsImage &&
95 IsSampler == other.IsSampler &&
96 Target == other.Target &&
97 ArrayLength == other.ArrayLength &&
98 MatchesPools(other._texturePool, other._samplerPool);
99 }
100
101 public override bool Equals(object obj)
102 {
103 return obj is CacheEntryFromBufferKey other && Equals(other);
104 }
105
106 public override int GetHashCode()
107 {
108 return HashCode.Combine(_texturePool, _samplerPool, IsSampler);
109 }
110 }
111
112 /// <summary>
113 /// Array cache entry key.
114 /// </summary>
115 private readonly struct CacheEntryFromBufferKey : IEquatable<CacheEntryFromBufferKey>
30 { 116 {
31 /// <summary> 117 /// <summary>
32 /// Whether the entry is for an image. 118 /// Whether the entry is for an image.
@@ -61,7 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Image
61 /// <param name="texturePool">Texture pool where the array textures are located</param> 147 /// <param name="texturePool">Texture pool where the array textures are located</param>
62 /// <param name="samplerPool">Sampler pool where the array samplers are located</param> 148 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
63 /// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param> 149 /// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param>
64 public CacheEntryKey( 150 public CacheEntryFromBufferKey(
65 bool isImage, 151 bool isImage,
66 TextureBindingInfo bindingInfo, 152 TextureBindingInfo bindingInfo,
67 TexturePool texturePool, 153 TexturePool texturePool,
@@ -100,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Image
100 return _textureBufferBounds.Equals(textureBufferBounds); 186 return _textureBufferBounds.Equals(textureBufferBounds);
101 } 187 }
102 188
103 public bool Equals(CacheEntryKey other) 189 public bool Equals(CacheEntryFromBufferKey other)
104 { 190 {
105 return IsImage == other.IsImage && 191 return IsImage == other.IsImage &&
106 Target == other.Target && 192 Target == other.Target &&
@@ -112,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu.Image
112 198
113 public override bool Equals(object obj) 199 public override bool Equals(object obj)
114 { 200 {
115 return obj is CacheEntryKey other && Equals(other); 201 return obj is CacheEntryFromBufferKey other && Equals(other);
116 } 202 }
117 203
118 public override int GetHashCode() 204 public override int GetHashCode()
@@ -122,88 +208,58 @@ namespace Ryujinx.Graphics.Gpu.Image
122 } 208 }
123 209
124 /// <summary> 210 /// <summary>
125 /// Array cache entry. 211 /// Array cache entry from pool.
126 /// </summary> 212 /// </summary>
127 private class CacheEntry 213 private class CacheEntry
128 { 214 {
129 /// <summary> 215 /// <summary>
130 /// Key for this entry on the cache.
131 /// </summary>
132 public readonly CacheEntryKey Key;
133
134 /// <summary>
135 /// Linked list node used on the texture bindings array cache.
136 /// </summary>
137 public LinkedListNode<CacheEntry> CacheNode;
138
139 /// <summary>
140 /// Timestamp set on the last use of the array by the cache.
141 /// </summary>
142 public int CacheTimestamp;
143
144 /// <summary>
145 /// All cached textures, along with their invalidated sequence number as value. 216 /// All cached textures, along with their invalidated sequence number as value.
146 /// </summary> 217 /// </summary>
147 public readonly Dictionary<Texture, int> Textures; 218 public readonly Dictionary<Texture, int> Textures;
148 219
149 /// <summary> 220 /// <summary>
150 /// All pool texture IDs along with their textures. 221 /// Backend texture array if the entry is for a texture, otherwise null.
151 /// </summary> 222 /// </summary>
152 public readonly Dictionary<int, Texture> TextureIds; 223 public readonly ITextureArray TextureArray;
153 224
154 /// <summary> 225 /// <summary>
155 /// All pool sampler IDs along with their samplers. 226 /// Backend image array if the entry is for an image, otherwise null.
156 /// </summary> 227 /// </summary>
157 public readonly Dictionary<int, Sampler> SamplerIds; 228 public readonly IImageArray ImageArray;
158 229
159 /// <summary> 230 /// <summary>
160 /// Backend texture array if the entry is for a texture, otherwise null. 231 /// Texture pool where the array textures are located.
161 /// </summary> 232 /// </summary>
162 public readonly ITextureArray TextureArray; 233 protected readonly TexturePool TexturePool;
163 234
164 /// <summary> 235 /// <summary>
165 /// Backend image array if the entry is for an image, otherwise null. 236 /// Sampler pool where the array samplers are located.
166 /// </summary> 237 /// </summary>
167 public readonly IImageArray ImageArray; 238 protected readonly SamplerPool SamplerPool;
168
169 private readonly TexturePool _texturePool;
170 private readonly SamplerPool _samplerPool;
171 239
172 private int _texturePoolSequence; 240 private int _texturePoolSequence;
173 private int _samplerPoolSequence; 241 private int _samplerPoolSequence;
174 242
175 private int[] _cachedTextureBuffer;
176 private int[] _cachedSamplerBuffer;
177
178 private int _lastSequenceNumber;
179
180 /// <summary> 243 /// <summary>
181 /// Creates a new array cache entry. 244 /// Creates a new array cache entry.
182 /// </summary> 245 /// </summary>
183 /// <param name="key">Key for this entry on the cache</param>
184 /// <param name="texturePool">Texture pool where the array textures are located</param> 246 /// <param name="texturePool">Texture pool where the array textures are located</param>
185 /// <param name="samplerPool">Sampler pool where the array samplers are located</param> 247 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
186 private CacheEntry(ref CacheEntryKey key, TexturePool texturePool, SamplerPool samplerPool) 248 private CacheEntry(TexturePool texturePool, SamplerPool samplerPool)
187 { 249 {
188 Key = key;
189 Textures = new Dictionary<Texture, int>(); 250 Textures = new Dictionary<Texture, int>();
190 TextureIds = new Dictionary<int, Texture>();
191 SamplerIds = new Dictionary<int, Sampler>();
192 251
193 _texturePool = texturePool; 252 TexturePool = texturePool;
194 _samplerPool = samplerPool; 253 SamplerPool = samplerPool;
195
196 _lastSequenceNumber = -1;
197 } 254 }
198 255
199 /// <summary> 256 /// <summary>
200 /// Creates a new array cache entry. 257 /// Creates a new array cache entry.
201 /// </summary> 258 /// </summary>
202 /// <param name="key">Key for this entry on the cache</param>
203 /// <param name="array">Backend texture array</param> 259 /// <param name="array">Backend texture array</param>
204 /// <param name="texturePool">Texture pool where the array textures are located</param> 260 /// <param name="texturePool">Texture pool where the array textures are located</param>
205 /// <param name="samplerPool">Sampler pool where the array samplers are located</param> 261 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
206 public CacheEntry(ref CacheEntryKey key, ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : this(ref key, texturePool, samplerPool) 262 public CacheEntry(ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : this(texturePool, samplerPool)
207 { 263 {
208 TextureArray = array; 264 TextureArray = array;
209 } 265 }
@@ -211,11 +267,10 @@ namespace Ryujinx.Graphics.Gpu.Image
211 /// <summary> 267 /// <summary>
212 /// Creates a new array cache entry. 268 /// Creates a new array cache entry.
213 /// </summary> 269 /// </summary>
214 /// <param name="key">Key for this entry on the cache</param>
215 /// <param name="array">Backend image array</param> 270 /// <param name="array">Backend image array</param>
216 /// <param name="texturePool">Texture pool where the array textures are located</param> 271 /// <param name="texturePool">Texture pool where the array textures are located</param>
217 /// <param name="samplerPool">Sampler pool where the array samplers are located</param> 272 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
218 public CacheEntry(ref CacheEntryKey key, IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : this(ref key, texturePool, samplerPool) 273 public CacheEntry(IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : this(texturePool, samplerPool)
219 { 274 {
220 ImageArray = array; 275 ImageArray = array;
221 } 276 }
@@ -248,23 +303,9 @@ namespace Ryujinx.Graphics.Gpu.Image
248 /// <summary> 303 /// <summary>
249 /// Clears all cached texture instances. 304 /// Clears all cached texture instances.
250 /// </summary> 305 /// </summary>
251 public void Reset() 306 public virtual void Reset()
252 { 307 {
253 Textures.Clear(); 308 Textures.Clear();
254 TextureIds.Clear();
255 SamplerIds.Clear();
256 }
257
258 /// <summary>
259 /// Updates the cached constant buffer data.
260 /// </summary>
261 /// <param name="cachedTextureBuffer">Constant buffer data with the texture handles (and sampler handles, if they are combined)</param>
262 /// <param name="cachedSamplerBuffer">Constant buffer data with the sampler handles</param>
263 /// <param name="separateSamplerBuffer">Whether <paramref name="cachedTextureBuffer"/> and <paramref name="cachedSamplerBuffer"/> comes from different buffers</param>
264 public void UpdateData(ReadOnlySpan<int> cachedTextureBuffer, ReadOnlySpan<int> cachedSamplerBuffer, bool separateSamplerBuffer)
265 {
266 _cachedTextureBuffer = cachedTextureBuffer.ToArray();
267 _cachedSamplerBuffer = separateSamplerBuffer ? cachedSamplerBuffer.ToArray() : _cachedTextureBuffer;
268 } 309 }
269 310
270 /// <summary> 311 /// <summary>
@@ -287,39 +328,105 @@ namespace Ryujinx.Graphics.Gpu.Image
287 /// <summary> 328 /// <summary>
288 /// Checks if the cached texture or sampler pool has been modified since the last call to this method. 329 /// Checks if the cached texture or sampler pool has been modified since the last call to this method.
289 /// </summary> 330 /// </summary>
290 /// <returns>True if any used entries of the pools might have been modified, false otherwise</returns> 331 /// <returns>True if any used entries of the pool might have been modified, false otherwise</returns>
291 public bool PoolsModified() 332 public bool TexturePoolModified()
292 { 333 {
293 bool texturePoolModified = _texturePool.WasModified(ref _texturePoolSequence); 334 return TexturePool.WasModified(ref _texturePoolSequence);
294 bool samplerPoolModified = _samplerPool.WasModified(ref _samplerPoolSequence); 335 }
295 336
296 // If both pools were not modified since the last check, we have nothing else to check. 337 /// <summary>
297 if (!texturePoolModified && !samplerPoolModified) 338 /// Checks if the cached texture or sampler pool has been modified since the last call to this method.
298 { 339 /// </summary>
299 return false; 340 /// <returns>True if any used entries of the pool might have been modified, false otherwise</returns>
300 } 341 public bool SamplerPoolModified()
342 {
343 return SamplerPool.WasModified(ref _samplerPoolSequence);
344 }
345 }
301 346
302 // If the pools were modified, let's check if any of the entries we care about changed. 347 /// <summary>
348 /// Array cache entry from constant buffer.
349 /// </summary>
350 private class CacheEntryFromBuffer : CacheEntry
351 {
352 /// <summary>
353 /// Key for this entry on the cache.
354 /// </summary>
355 public readonly CacheEntryFromBufferKey Key;
303 356
304 // Check if any of our cached textures changed on the pool. 357 /// <summary>
305 foreach ((int textureId, Texture texture) in TextureIds) 358 /// Linked list node used on the texture bindings array cache.
306 { 359 /// </summary>
307 if (_texturePool.GetCachedItem(textureId) != texture) 360 public LinkedListNode<CacheEntryFromBuffer> CacheNode;
308 {
309 return true;
310 }
311 }
312 361
313 // Check if any of our cached samplers changed on the pool. 362 /// <summary>
314 foreach ((int samplerId, Sampler sampler) in SamplerIds) 363 /// Timestamp set on the last use of the array by the cache.
315 { 364 /// </summary>
316 if (_samplerPool.GetCachedItem(samplerId) != sampler) 365 public int CacheTimestamp;
317 {
318 return true;
319 }
320 }
321 366
322 return false; 367 /// <summary>
368 /// All pool texture IDs along with their textures.
369 /// </summary>
370 public readonly Dictionary<int, (Texture, TextureDescriptor)> TextureIds;
371
372 /// <summary>
373 /// All pool sampler IDs along with their samplers.
374 /// </summary>
375 public readonly Dictionary<int, (Sampler, SamplerDescriptor)> SamplerIds;
376
377 private int[] _cachedTextureBuffer;
378 private int[] _cachedSamplerBuffer;
379
380 private int _lastSequenceNumber;
381
382 /// <summary>
383 /// Creates a new array cache entry.
384 /// </summary>
385 /// <param name="key">Key for this entry on the cache</param>
386 /// <param name="array">Backend texture array</param>
387 /// <param name="texturePool">Texture pool where the array textures are located</param>
388 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
389 public CacheEntryFromBuffer(ref CacheEntryFromBufferKey key, ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : base(array, texturePool, samplerPool)
390 {
391 Key = key;
392 _lastSequenceNumber = -1;
393 TextureIds = new Dictionary<int, (Texture, TextureDescriptor)>();
394 SamplerIds = new Dictionary<int, (Sampler, SamplerDescriptor)>();
395 }
396
397 /// <summary>
398 /// Creates a new array cache entry.
399 /// </summary>
400 /// <param name="key">Key for this entry on the cache</param>
401 /// <param name="array">Backend image array</param>
402 /// <param name="texturePool">Texture pool where the array textures are located</param>
403 /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
404 public CacheEntryFromBuffer(ref CacheEntryFromBufferKey key, IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : base(array, texturePool, samplerPool)
405 {
406 Key = key;
407 _lastSequenceNumber = -1;
408 TextureIds = new Dictionary<int, (Texture, TextureDescriptor)>();
409 SamplerIds = new Dictionary<int, (Sampler, SamplerDescriptor)>();
410 }
411
412 /// <inheritdoc/>
413 public override void Reset()
414 {
415 base.Reset();
416 TextureIds.Clear();
417 SamplerIds.Clear();
418 }
419
420 /// <summary>
421 /// Updates the cached constant buffer data.
422 /// </summary>
423 /// <param name="cachedTextureBuffer">Constant buffer data with the texture handles (and sampler handles, if they are combined)</param>
424 /// <param name="cachedSamplerBuffer">Constant buffer data with the sampler handles</param>
425 /// <param name="separateSamplerBuffer">Whether <paramref name="cachedTextureBuffer"/> and <paramref name="cachedSamplerBuffer"/> comes from different buffers</param>
426 public void UpdateData(ReadOnlySpan<int> cachedTextureBuffer, ReadOnlySpan<int> cachedSamplerBuffer, bool separateSamplerBuffer)
427 {
428 _cachedTextureBuffer = cachedTextureBuffer.ToArray();
429 _cachedSamplerBuffer = separateSamplerBuffer ? cachedSamplerBuffer.ToArray() : _cachedTextureBuffer;
323 } 430 }
324 431
325 /// <summary> 432 /// <summary>
@@ -380,10 +487,51 @@ namespace Ryujinx.Graphics.Gpu.Image
380 487
381 return true; 488 return true;
382 } 489 }
490
491 /// <summary>
492 /// Checks if the cached texture or sampler pool has been modified since the last call to this method.
493 /// </summary>
494 /// <returns>True if any used entries of the pools might have been modified, false otherwise</returns>
495 public bool PoolsModified()
496 {
497 bool texturePoolModified = TexturePoolModified();
498 bool samplerPoolModified = SamplerPoolModified();
499
500 // If both pools were not modified since the last check, we have nothing else to check.
501 if (!texturePoolModified && !samplerPoolModified)
502 {
503 return false;
504 }
505
506 // If the pools were modified, let's check if any of the entries we care about changed.
507
508 // Check if any of our cached textures changed on the pool.
509 foreach ((int textureId, (Texture texture, TextureDescriptor descriptor)) in TextureIds)
510 {
511 if (TexturePool.GetCachedItem(textureId) != texture ||
512 (texture == null && TexturePool.IsValidId(textureId) && !TexturePool.GetDescriptorRef(textureId).Equals(descriptor)))
513 {
514 return true;
515 }
516 }
517
518 // Check if any of our cached samplers changed on the pool.
519 foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
520 {
521 if (SamplerPool.GetCachedItem(samplerId) != sampler ||
522 (sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
523 {
524 return true;
525 }
526 }
527
528 return false;
529 }
383 } 530 }
384 531
385 private readonly Dictionary<CacheEntryKey, CacheEntry> _cache; 532 private readonly Dictionary<CacheEntryFromBufferKey, CacheEntryFromBuffer> _cacheFromBuffer;
386 private readonly LinkedList<CacheEntry> _lruCache; 533 private readonly Dictionary<CacheEntryFromPoolKey, CacheEntry> _cacheFromPool;
534 private readonly LinkedList<CacheEntryFromBuffer> _lruCache;
387 535
388 private int _currentTimestamp; 536 private int _currentTimestamp;
389 537
@@ -392,14 +540,13 @@ namespace Ryujinx.Graphics.Gpu.Image
392 /// </summary> 540 /// </summary>
393 /// <param name="context">GPU context</param> 541 /// <param name="context">GPU context</param>
394 /// <param name="channel">GPU channel</param> 542 /// <param name="channel">GPU channel</param>
395 /// <param name="isCompute">Whether the bindings will be used for compute or graphics pipelines</param> 543 public TextureBindingsArrayCache(GpuContext context, GpuChannel channel)
396 public TextureBindingsArrayCache(GpuContext context, GpuChannel channel, bool isCompute)
397 { 544 {
398 _context = context; 545 _context = context;
399 _channel = channel; 546 _channel = channel;
400 _isCompute = isCompute; 547 _cacheFromBuffer = new Dictionary<CacheEntryFromBufferKey, CacheEntryFromBuffer>();
401 _cache = new Dictionary<CacheEntryKey, CacheEntry>(); 548 _cacheFromPool = new Dictionary<CacheEntryFromPoolKey, CacheEntry>();
402 _lruCache = new LinkedList<CacheEntry>(); 549 _lruCache = new LinkedList<CacheEntryFromBuffer>();
403 } 550 }
404 551
405 /// <summary> 552 /// <summary>
@@ -458,14 +605,179 @@ namespace Ryujinx.Graphics.Gpu.Image
458 SamplerIndex samplerIndex, 605 SamplerIndex samplerIndex,
459 TextureBindingInfo bindingInfo) 606 TextureBindingInfo bindingInfo)
460 { 607 {
608 if (IsDirectHandleType(bindingInfo.Handle))
609 {
610 UpdateFromPool(texturePool, samplerPool, stage, isImage, bindingInfo);
611 }
612 else
613 {
614 UpdateFromBuffer(texturePool, samplerPool, stage, stageIndex, textureBufferIndex, isImage, samplerIndex, bindingInfo);
615 }
616 }
617
618 /// <summary>
619 /// Updates a texture or image array bindings and textures from a texture or sampler pool.
620 /// </summary>
621 /// <param name="texturePool">Texture pool</param>
622 /// <param name="samplerPool">Sampler pool</param>
623 /// <param name="stage">Shader stage where the array is used</param>
624 /// <param name="isImage">Whether the array is a image or texture array</param>
625 /// <param name="bindingInfo">Array binding information</param>
626 private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, TextureBindingInfo bindingInfo)
627 {
628 CacheEntry entry = GetOrAddEntry(texturePool, samplerPool, bindingInfo, isImage, out bool isNewEntry);
629
630 bool isSampler = bindingInfo.IsSamplerOnly;
631 bool poolModified = isSampler ? entry.SamplerPoolModified() : entry.TexturePoolModified();
632 bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
633 bool resScaleUnsupported = bindingInfo.Flags.HasFlag(TextureUsageFlags.ResScaleUnsupported);
634
635 if (!poolModified && !isNewEntry && entry.ValidateTextures())
636 {
637 entry.SynchronizeMemory(isStore, resScaleUnsupported);
638
639 if (isImage)
640 {
641 _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray);
642 }
643 else
644 {
645 _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray);
646 }
647
648 return;
649 }
650
651 if (!isNewEntry)
652 {
653 entry.Reset();
654 }
655
656 int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
657 length = Math.Min(length, bindingInfo.ArrayLength);
658
659 Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
660 ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
661 ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
662
663 for (int index = 0; index < length; index++)
664 {
665 Texture texture = null;
666 Sampler sampler = null;
667
668 if (isSampler)
669 {
670 sampler = samplerPool?.Get(index);
671 }
672 else
673 {
674 ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture);
675
676 if (texture != null)
677 {
678 entry.Textures[texture] = texture.InvalidatedSequence;
679
680 if (isStore)
681 {
682 texture.SignalModified();
683 }
684
685 if (resScaleUnsupported && texture.ScaleMode != TextureScaleMode.Blacklisted)
686 {
687 // Scaling textures used on arrays is currently not supported.
688
689 texture.BlacklistScale();
690 }
691 }
692 }
693
694 ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
695 ISampler hostSampler = sampler?.GetHostSampler(texture);
696
697 Format format = bindingInfo.Format;
698
699 if (hostTexture != null && texture.Target == Target.TextureBuffer)
700 {
701 // Ensure that the buffer texture is using the correct buffer as storage.
702 // Buffers are frequently re-created to accommodate larger data, so we need to re-bind
703 // to ensure we're not using a old buffer that was already deleted.
704 if (isImage)
705 {
706 if (format == 0 && texture != null)
707 {
708 format = texture.Format;
709 }
710
711 _channel.BufferManager.SetBufferTextureStorage(entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
712 }
713 else
714 {
715 _channel.BufferManager.SetBufferTextureStorage(entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
716 }
717 }
718 else if (isImage)
719 {
720 if (format == 0 && texture != null)
721 {
722 format = texture.Format;
723 }
724
725 formats[index] = format;
726 textures[index] = hostTexture;
727 }
728 else
729 {
730 samplers[index] = hostSampler;
731 textures[index] = hostTexture;
732 }
733 }
734
735 if (isImage)
736 {
737 entry.ImageArray.SetFormats(0, formats);
738 entry.ImageArray.SetImages(0, textures);
739
740 _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray);
741 }
742 else
743 {
744 entry.TextureArray.SetSamplers(0, samplers);
745 entry.TextureArray.SetTextures(0, textures);
746
747 _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray);
748 }
749 }
750
751 /// <summary>
752 /// Updates a texture or image array bindings and textures from constant buffer handles.
753 /// </summary>
754 /// <param name="texturePool">Texture pool</param>
755 /// <param name="samplerPool">Sampler pool</param>
756 /// <param name="stage">Shader stage where the array is used</param>
757 /// <param name="stageIndex">Shader stage index where the array is used</param>
758 /// <param name="textureBufferIndex">Texture constant buffer index</param>
759 /// <param name="isImage">Whether the array is a image or texture array</param>
760 /// <param name="samplerIndex">Sampler handles source</param>
761 /// <param name="bindingInfo">Array binding information</param>
762 private void UpdateFromBuffer(
763 TexturePool texturePool,
764 SamplerPool samplerPool,
765 ShaderStage stage,
766 int stageIndex,
767 int textureBufferIndex,
768 bool isImage,
769 SamplerIndex samplerIndex,
770 TextureBindingInfo bindingInfo)
771 {
461 (textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex); 772 (textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex);
462 773
463 bool separateSamplerBuffer = textureBufferIndex != samplerBufferIndex; 774 bool separateSamplerBuffer = textureBufferIndex != samplerBufferIndex;
775 bool isCompute = stage == ShaderStage.Compute;
464 776
465 ref BufferBounds textureBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex); 777 ref BufferBounds textureBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
466 ref BufferBounds samplerBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex); 778 ref BufferBounds samplerBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
467 779
468 CacheEntry entry = GetOrAddEntry( 780 CacheEntryFromBuffer entry = GetOrAddEntry(
469 texturePool, 781 texturePool,
470 samplerPool, 782 samplerPool,
471 bindingInfo, 783 bindingInfo,
@@ -589,8 +901,8 @@ namespace Ryujinx.Graphics.Gpu.Image
589 901
590 Sampler sampler = samplerPool?.Get(samplerId); 902 Sampler sampler = samplerPool?.Get(samplerId);
591 903
592 entry.TextureIds[textureId] = texture; 904 entry.TextureIds[textureId] = (texture, descriptor);
593 entry.SamplerIds[samplerId] = sampler; 905 entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
594 906
595 ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); 907 ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
596 ISampler hostSampler = sampler?.GetHostSampler(texture); 908 ISampler hostSampler = sampler?.GetHostSampler(texture);
@@ -650,13 +962,12 @@ namespace Ryujinx.Graphics.Gpu.Image
650 } 962 }
651 963
652 /// <summary> 964 /// <summary>
653 /// Gets a cached texture entry, or creates a new one if not found. 965 /// Gets a cached texture entry from pool, or creates a new one if not found.
654 /// </summary> 966 /// </summary>
655 /// <param name="texturePool">Texture pool</param> 967 /// <param name="texturePool">Texture pool</param>
656 /// <param name="samplerPool">Sampler pool</param> 968 /// <param name="samplerPool">Sampler pool</param>
657 /// <param name="bindingInfo">Array binding information</param> 969 /// <param name="bindingInfo">Array binding information</param>
658 /// <param name="isImage">Whether the array is a image or texture array</param> 970 /// <param name="isImage">Whether the array is a image or texture array</param>
659 /// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param>
660 /// <param name="isNew">Whether a new entry was created, or an existing one was returned</param> 971 /// <param name="isNew">Whether a new entry was created, or an existing one was returned</param>
661 /// <returns>Cache entry</returns> 972 /// <returns>Cache entry</returns>
662 private CacheEntry GetOrAddEntry( 973 private CacheEntry GetOrAddEntry(
@@ -664,17 +975,59 @@ namespace Ryujinx.Graphics.Gpu.Image
664 SamplerPool samplerPool, 975 SamplerPool samplerPool,
665 TextureBindingInfo bindingInfo, 976 TextureBindingInfo bindingInfo,
666 bool isImage, 977 bool isImage,
978 out bool isNew)
979 {
980 CacheEntryFromPoolKey key = new CacheEntryFromPoolKey(isImage, bindingInfo, texturePool, samplerPool);
981
982 isNew = !_cacheFromPool.TryGetValue(key, out CacheEntry entry);
983
984 if (isNew)
985 {
986 int arrayLength = bindingInfo.ArrayLength;
987
988 if (isImage)
989 {
990 IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
991
992 _cacheFromPool.Add(key, entry = new CacheEntry(array, texturePool, samplerPool));
993 }
994 else
995 {
996 ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
997
998 _cacheFromPool.Add(key, entry = new CacheEntry(array, texturePool, samplerPool));
999 }
1000 }
1001
1002 return entry;
1003 }
1004
1005 /// <summary>
1006 /// Gets a cached texture entry from constant buffer, or creates a new one if not found.
1007 /// </summary>
1008 /// <param name="texturePool">Texture pool</param>
1009 /// <param name="samplerPool">Sampler pool</param>
1010 /// <param name="bindingInfo">Array binding information</param>
1011 /// <param name="isImage">Whether the array is a image or texture array</param>
1012 /// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param>
1013 /// <param name="isNew">Whether a new entry was created, or an existing one was returned</param>
1014 /// <returns>Cache entry</returns>
1015 private CacheEntryFromBuffer GetOrAddEntry(
1016 TexturePool texturePool,
1017 SamplerPool samplerPool,
1018 TextureBindingInfo bindingInfo,
1019 bool isImage,
667 ref BufferBounds textureBufferBounds, 1020 ref BufferBounds textureBufferBounds,
668 out bool isNew) 1021 out bool isNew)
669 { 1022 {
670 CacheEntryKey key = new CacheEntryKey( 1023 CacheEntryFromBufferKey key = new CacheEntryFromBufferKey(
671 isImage, 1024 isImage,
672 bindingInfo, 1025 bindingInfo,
673 texturePool, 1026 texturePool,
674 samplerPool, 1027 samplerPool,
675 ref textureBufferBounds); 1028 ref textureBufferBounds);
676 1029
677 isNew = !_cache.TryGetValue(key, out CacheEntry entry); 1030 isNew = !_cacheFromBuffer.TryGetValue(key, out CacheEntryFromBuffer entry);
678 1031
679 if (isNew) 1032 if (isNew)
680 { 1033 {
@@ -684,13 +1037,13 @@ namespace Ryujinx.Graphics.Gpu.Image
684 { 1037 {
685 IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); 1038 IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
686 1039
687 _cache.Add(key, entry = new CacheEntry(ref key, array, texturePool, samplerPool)); 1040 _cacheFromBuffer.Add(key, entry = new CacheEntryFromBuffer(ref key, array, texturePool, samplerPool));
688 } 1041 }
689 else 1042 else
690 { 1043 {
691 ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); 1044 ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
692 1045
693 _cache.Add(key, entry = new CacheEntry(ref key, array, texturePool, samplerPool)); 1046 _cacheFromBuffer.Add(key, entry = new CacheEntryFromBuffer(ref key, array, texturePool, samplerPool));
694 } 1047 }
695 } 1048 }
696 1049
@@ -716,15 +1069,52 @@ namespace Ryujinx.Graphics.Gpu.Image
716 /// </summary> 1069 /// </summary>
717 private void RemoveLeastUsedEntries() 1070 private void RemoveLeastUsedEntries()
718 { 1071 {
719 LinkedListNode<CacheEntry> nextNode = _lruCache.First; 1072 LinkedListNode<CacheEntryFromBuffer> nextNode = _lruCache.First;
720 1073
721 while (nextNode != null && _currentTimestamp - nextNode.Value.CacheTimestamp >= MinDeltaForRemoval) 1074 while (nextNode != null && _currentTimestamp - nextNode.Value.CacheTimestamp >= MinDeltaForRemoval)
722 { 1075 {
723 LinkedListNode<CacheEntry> toRemove = nextNode; 1076 LinkedListNode<CacheEntryFromBuffer> toRemove = nextNode;
724 nextNode = nextNode.Next; 1077 nextNode = nextNode.Next;
725 _cache.Remove(toRemove.Value.Key); 1078 _cacheFromBuffer.Remove(toRemove.Value.Key);
726 _lruCache.Remove(toRemove); 1079 _lruCache.Remove(toRemove);
727 } 1080 }
728 } 1081 }
1082
1083 /// <summary>
1084 /// Removes all cached texture arrays matching the specified texture pool.
1085 /// </summary>
1086 /// <param name="pool">Texture pool</param>
1087 public void RemoveAllWithPool<T>(IPool<T> pool)
1088 {
1089 List<CacheEntryFromPoolKey> keysToRemove = null;
1090
1091 foreach (CacheEntryFromPoolKey key in _cacheFromPool.Keys)
1092 {
1093 if (key.MatchesPool(pool))
1094 {
1095 (keysToRemove ??= new()).Add(key);
1096 }
1097 }
1098
1099 if (keysToRemove != null)
1100 {
1101 foreach (CacheEntryFromPoolKey key in keysToRemove)
1102 {
1103 _cacheFromPool.Remove(key);
1104 }
1105 }
1106 }
1107
1108 /// <summary>
1109 /// Checks if a handle indicates the binding should have all its textures sourced directly from a pool.
1110 /// </summary>
1111 /// <param name="handle">Handle to check</param>
1112 /// <returns>True if the handle represents direct pool access, false otherwise</returns>
1113 private static bool IsDirectHandleType(int handle)
1114 {
1115 (_, _, TextureHandleType type) = TextureHandle.UnpackOffsets(handle);
1116
1117 return type == TextureHandleType.Direct;
1118 }
729 } 1119 }
730} 1120}
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index a1dde673b..9f1f60d95 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Image
34 private readonly TexturePoolCache _texturePoolCache; 34 private readonly TexturePoolCache _texturePoolCache;
35 private readonly SamplerPoolCache _samplerPoolCache; 35 private readonly SamplerPoolCache _samplerPoolCache;
36 36
37 private readonly TextureBindingsArrayCache _arrayBindingsCache; 37 private readonly TextureBindingsArrayCache _bindingsArrayCache;
38 38
39 private TexturePool _cachedTexturePool; 39 private TexturePool _cachedTexturePool;
40 private SamplerPool _cachedSamplerPool; 40 private SamplerPool _cachedSamplerPool;
@@ -72,12 +72,14 @@ namespace Ryujinx.Graphics.Gpu.Image
72 /// </summary> 72 /// </summary>
73 /// <param name="context">The GPU context that the texture bindings manager belongs to</param> 73 /// <param name="context">The GPU context that the texture bindings manager belongs to</param>
74 /// <param name="channel">The GPU channel that the texture bindings manager belongs to</param> 74 /// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
75 /// <param name="bindingsArrayCache">Cache of texture array bindings</param>
75 /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param> 76 /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
76 /// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param> 77 /// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param>
77 /// <param name="isCompute">True if the bindings manager is used for the compute engine</param> 78 /// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
78 public TextureBindingsManager( 79 public TextureBindingsManager(
79 GpuContext context, 80 GpuContext context,
80 GpuChannel channel, 81 GpuChannel channel,
82 TextureBindingsArrayCache bindingsArrayCache,
81 TexturePoolCache texturePoolCache, 83 TexturePoolCache texturePoolCache,
82 SamplerPoolCache samplerPoolCache, 84 SamplerPoolCache samplerPoolCache,
83 bool isCompute) 85 bool isCompute)
@@ -89,7 +91,7 @@ namespace Ryujinx.Graphics.Gpu.Image
89 91
90 _isCompute = isCompute; 92 _isCompute = isCompute;
91 93
92 _arrayBindingsCache = new TextureBindingsArrayCache(context, channel, isCompute); 94 _bindingsArrayCache = bindingsArrayCache;
93 95
94 int stages = isCompute ? 1 : Constants.ShaderStages; 96 int stages = isCompute ? 1 : Constants.ShaderStages;
95 97
@@ -456,7 +458,7 @@ namespace Ryujinx.Graphics.Gpu.Image
456 458
457 if (bindingInfo.ArrayLength > 1) 459 if (bindingInfo.ArrayLength > 1)
458 { 460 {
459 _arrayBindingsCache.UpdateTextureArray(texturePool, samplerPool, stage, stageIndex, _textureBufferIndex, _samplerIndex, bindingInfo); 461 _bindingsArrayCache.UpdateTextureArray(texturePool, samplerPool, stage, stageIndex, _textureBufferIndex, _samplerIndex, bindingInfo);
460 462
461 continue; 463 continue;
462 } 464 }
@@ -594,7 +596,7 @@ namespace Ryujinx.Graphics.Gpu.Image
594 596
595 if (bindingInfo.ArrayLength > 1) 597 if (bindingInfo.ArrayLength > 1)
596 { 598 {
597 _arrayBindingsCache.UpdateImageArray(pool, stage, stageIndex, _textureBufferIndex, bindingInfo); 599 _bindingsArrayCache.UpdateImageArray(pool, stage, stageIndex, _textureBufferIndex, bindingInfo);
598 600
599 continue; 601 continue;
600 } 602 }
@@ -732,7 +734,7 @@ namespace Ryujinx.Graphics.Gpu.Image
732 734
733 ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); 735 ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
734 736
735 TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId); 737 TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
736 738
737 TextureDescriptor descriptor; 739 TextureDescriptor descriptor;
738 740
@@ -828,7 +830,7 @@ namespace Ryujinx.Graphics.Gpu.Image
828 830
829 if (poolAddress != MemoryManager.PteUnmapped) 831 if (poolAddress != MemoryManager.PteUnmapped)
830 { 832 {
831 texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId); 833 texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
832 _texturePool = texturePool; 834 _texturePool = texturePool;
833 } 835 }
834 } 836 }
@@ -839,7 +841,7 @@ namespace Ryujinx.Graphics.Gpu.Image
839 841
840 if (poolAddress != MemoryManager.PteUnmapped) 842 if (poolAddress != MemoryManager.PteUnmapped)
841 { 843 {
842 samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId); 844 samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
843 _samplerPool = samplerPool; 845 _samplerPool = samplerPool;
844 } 846 }
845 } 847 }
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 8c2a88727..db2921468 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Image
15 15
16 private readonly TextureBindingsManager _cpBindingsManager; 16 private readonly TextureBindingsManager _cpBindingsManager;
17 private readonly TextureBindingsManager _gpBindingsManager; 17 private readonly TextureBindingsManager _gpBindingsManager;
18 private readonly TextureBindingsArrayCache _bindingsArrayCache;
18 private readonly TexturePoolCache _texturePoolCache; 19 private readonly TexturePoolCache _texturePoolCache;
19 private readonly SamplerPoolCache _samplerPoolCache; 20 private readonly SamplerPoolCache _samplerPoolCache;
20 21
@@ -46,8 +47,9 @@ namespace Ryujinx.Graphics.Gpu.Image
46 TexturePoolCache texturePoolCache = new(context); 47 TexturePoolCache texturePoolCache = new(context);
47 SamplerPoolCache samplerPoolCache = new(context); 48 SamplerPoolCache samplerPoolCache = new(context);
48 49
49 _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: true); 50 _bindingsArrayCache = new TextureBindingsArrayCache(context, channel);
50 _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: false); 51 _cpBindingsManager = new TextureBindingsManager(context, channel, _bindingsArrayCache, texturePoolCache, samplerPoolCache, isCompute: true);
52 _gpBindingsManager = new TextureBindingsManager(context, channel, _bindingsArrayCache, texturePoolCache, samplerPoolCache, isCompute: false);
51 _texturePoolCache = texturePoolCache; 53 _texturePoolCache = texturePoolCache;
52 _samplerPoolCache = samplerPoolCache; 54 _samplerPoolCache = samplerPoolCache;
53 55
@@ -384,7 +386,7 @@ namespace Ryujinx.Graphics.Gpu.Image
384 { 386 {
385 ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); 387 ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
386 388
387 TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId); 389 TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
388 390
389 return texturePool; 391 return texturePool;
390 } 392 }
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
index 6e36753e8..a80dcbc87 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs
@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
58 58
59 TextureBindings[i] = stage.Info.Textures.Select(descriptor => 59 TextureBindings[i] = stage.Info.Textures.Select(descriptor =>
60 { 60 {
61 Target target = ShaderTexture.GetTarget(descriptor.Type); 61 Target target = descriptor.Type != SamplerType.None ? ShaderTexture.GetTarget(descriptor.Type) : default;
62 62
63 var result = new TextureBindingInfo( 63 var result = new TextureBindingInfo(
64 target, 64 target,
@@ -66,7 +66,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
66 descriptor.ArrayLength, 66 descriptor.ArrayLength,
67 descriptor.CbufSlot, 67 descriptor.CbufSlot,
68 descriptor.HandleIndex, 68 descriptor.HandleIndex,
69 descriptor.Flags); 69 descriptor.Flags,
70 descriptor.Type == SamplerType.None);
70 71
71 if (descriptor.ArrayLength <= 1) 72 if (descriptor.ArrayLength <= 1)
72 { 73 {
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
index 681838a9b..45f32e2d3 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
@@ -110,6 +110,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
110 } 110 }
111 111
112 /// <inheritdoc/> 112 /// <inheritdoc/>
113 /// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
114 public int QuerySamplerArrayLengthFromPool()
115 {
116 return QueryArrayLengthFromPool(isSampler: true);
117 }
118
119 /// <inheritdoc/>
113 public SamplerType QuerySamplerType(int handle, int cbufSlot) 120 public SamplerType QuerySamplerType(int handle, int cbufSlot)
114 { 121 {
115 _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot); 122 _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
@@ -117,6 +124,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
117 } 124 }
118 125
119 /// <inheritdoc/> 126 /// <inheritdoc/>
127 /// <exception cref="DiskCacheLoadException">Constant buffer derived length is not available on the cache</exception>
120 public int QueryTextureArrayLengthFromBuffer(int slot) 128 public int QueryTextureArrayLengthFromBuffer(int slot)
121 { 129 {
122 if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot)) 130 if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot))
@@ -131,6 +139,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
131 } 139 }
132 140
133 /// <inheritdoc/> 141 /// <inheritdoc/>
142 /// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
143 public int QueryTextureArrayLengthFromPool()
144 {
145 return QueryArrayLengthFromPool(isSampler: false);
146 }
147
148 /// <inheritdoc/>
134 public TextureFormat QueryTextureFormat(int handle, int cbufSlot) 149 public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
135 { 150 {
136 _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot); 151 _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot);
@@ -170,6 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
170 } 185 }
171 186
172 /// <inheritdoc/> 187 /// <inheritdoc/>
188 /// <exception cref="DiskCacheLoadException">Texture information is not available on the cache</exception>
173 public void RegisterTexture(int handle, int cbufSlot) 189 public void RegisterTexture(int handle, int cbufSlot)
174 { 190 {
175 if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot)) 191 if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot))
@@ -182,5 +198,24 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
182 bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); 198 bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot);
183 _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized); 199 _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized);
184 } 200 }
201
202 /// <summary>
203 /// Gets the cached texture or sampler pool capacity.
204 /// </summary>
205 /// <param name="isSampler">True to get sampler pool length, false for texture pool length</param>
206 /// <returns>Pool length</returns>
207 /// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
208 private int QueryArrayLengthFromPool(bool isSampler)
209 {
210 if (!_oldSpecState.TextureArrayFromPoolRegistered(isSampler))
211 {
212 throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength);
213 }
214
215 int arrayLength = _oldSpecState.GetTextureArrayFromPoolLength(isSampler);
216 _newSpecState.RegisterTextureArrayLengthFromPool(isSampler, arrayLength);
217
218 return arrayLength;
219 }
185 } 220 }
186} 221}
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index b6a277a2a..2c19cc4b9 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
22 private const ushort FileFormatVersionMajor = 1; 22 private const ushort FileFormatVersionMajor = 1;
23 private const ushort FileFormatVersionMinor = 2; 23 private const ushort FileFormatVersionMinor = 2;
24 private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; 24 private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
25 private const uint CodeGenVersion = 6489; 25 private const uint CodeGenVersion = 6577;
26 26
27 private const string SharedTocFileName = "shared.toc"; 27 private const string SharedTocFileName = "shared.toc";
28 private const string SharedDataFileName = "shared.data"; 28 private const string SharedDataFileName = "shared.data";
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 1d22ab933..04949690a 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -121,6 +121,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
121 } 121 }
122 122
123 /// <inheritdoc/> 123 /// <inheritdoc/>
124 public int QuerySamplerArrayLengthFromPool()
125 {
126 int length = _state.SamplerPoolMaximumId + 1;
127 _state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: true, length);
128
129 return length;
130 }
131
132 /// <inheritdoc/>
124 public SamplerType QuerySamplerType(int handle, int cbufSlot) 133 public SamplerType QuerySamplerType(int handle, int cbufSlot)
125 { 134 {
126 _state.SpecializationState?.RecordTextureSamplerType(_stageIndex, handle, cbufSlot); 135 _state.SpecializationState?.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
@@ -141,6 +150,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
141 return arrayLength; 150 return arrayLength;
142 } 151 }
143 152
153 /// <inheritdoc/>
154 public int QueryTextureArrayLengthFromPool()
155 {
156 int length = _state.PoolState.TexturePoolMaximumId + 1;
157 _state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: false, length);
158
159 return length;
160 }
161
144 //// <inheritdoc/> 162 //// <inheritdoc/>
145 public TextureFormat QueryTextureFormat(int handle, int cbufSlot) 163 public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
146 { 164 {
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index 06e5edf1e..0d562b0da 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -213,6 +213,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
213 213
214 public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats; 214 public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats;
215 215
216 public bool QueryHostSupportsSeparateSampler() => _context.Capabilities.SupportsSeparateSampler;
217
216 public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; 218 public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
217 219
218 public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence; 220 public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence;
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
index cfc4a2ccc..808bf1851 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
@@ -6,6 +6,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
6 class GpuAccessorState 6 class GpuAccessorState
7 { 7 {
8 /// <summary> 8 /// <summary>
9 /// Maximum ID that a sampler pool entry may have.
10 /// </summary>
11 public readonly int SamplerPoolMaximumId;
12
13 /// <summary>
9 /// GPU texture pool state. 14 /// GPU texture pool state.
10 /// </summary> 15 /// </summary>
11 public readonly GpuChannelPoolState PoolState; 16 public readonly GpuChannelPoolState PoolState;
@@ -38,18 +43,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
38 /// <summary> 43 /// <summary>
39 /// Creates a new GPU accessor state. 44 /// Creates a new GPU accessor state.
40 /// </summary> 45 /// </summary>
46 /// <param name="samplerPoolMaximumId">Maximum ID that a sampler pool entry may have</param>
41 /// <param name="poolState">GPU texture pool state</param> 47 /// <param name="poolState">GPU texture pool state</param>
42 /// <param name="computeState">GPU compute state, for compute shaders</param> 48 /// <param name="computeState">GPU compute state, for compute shaders</param>
43 /// <param name="graphicsState">GPU graphics state, for vertex, tessellation, geometry and fragment shaders</param> 49 /// <param name="graphicsState">GPU graphics state, for vertex, tessellation, geometry and fragment shaders</param>
44 /// <param name="specializationState">Shader specialization state (shared by all stages)</param> 50 /// <param name="specializationState">Shader specialization state (shared by all stages)</param>
45 /// <param name="transformFeedbackDescriptors">Transform feedback information, if the shader uses transform feedback. Otherwise, should be null</param> 51 /// <param name="transformFeedbackDescriptors">Transform feedback information, if the shader uses transform feedback. Otherwise, should be null</param>
46 public GpuAccessorState( 52 public GpuAccessorState(
53 int samplerPoolMaximumId,
47 GpuChannelPoolState poolState, 54 GpuChannelPoolState poolState,
48 GpuChannelComputeState computeState, 55 GpuChannelComputeState computeState,
49 GpuChannelGraphicsState graphicsState, 56 GpuChannelGraphicsState graphicsState,
50 ShaderSpecializationState specializationState, 57 ShaderSpecializationState specializationState,
51 TransformFeedbackDescriptor[] transformFeedbackDescriptors = null) 58 TransformFeedbackDescriptor[] transformFeedbackDescriptors = null)
52 { 59 {
60 SamplerPoolMaximumId = samplerPoolMaximumId;
53 PoolState = poolState; 61 PoolState = poolState;
54 GraphicsState = graphicsState; 62 GraphicsState = graphicsState;
55 ComputeState = computeState; 63 ComputeState = computeState;
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs
index ddb45152e..a2ab99335 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs
@@ -2,7 +2,6 @@ using System;
2 2
3namespace Ryujinx.Graphics.Gpu.Shader 3namespace Ryujinx.Graphics.Gpu.Shader
4{ 4{
5#pragma warning disable CS0659 // Class overrides Object.Equals(object o) but does not override Object.GetHashCode()
6 /// <summary> 5 /// <summary>
7 /// State used by the <see cref="GpuAccessor"/>. 6 /// State used by the <see cref="GpuAccessor"/>.
8 /// </summary> 7 /// </summary>
@@ -52,6 +51,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
52 { 51 {
53 return obj is GpuChannelPoolState state && Equals(state); 52 return obj is GpuChannelPoolState state && Equals(state);
54 } 53 }
54
55 public override int GetHashCode()
56 {
57 return HashCode.Combine(TexturePoolGpuVa, TexturePoolMaximumId, TextureBufferIndex);
58 }
55 } 59 }
56#pragma warning restore CS0659
57} 60}
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 0b17af8b2..31cc94a25 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -192,12 +192,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
192 /// This automatically translates, compiles and adds the code to the cache if not present. 192 /// This automatically translates, compiles and adds the code to the cache if not present.
193 /// </remarks> 193 /// </remarks>
194 /// <param name="channel">GPU channel</param> 194 /// <param name="channel">GPU channel</param>
195 /// <param name="samplerPoolMaximumId">Maximum ID that an entry in the sampler pool may have</param>
195 /// <param name="poolState">Texture pool state</param> 196 /// <param name="poolState">Texture pool state</param>
196 /// <param name="computeState">Compute engine state</param> 197 /// <param name="computeState">Compute engine state</param>
197 /// <param name="gpuVa">GPU virtual address of the binary shader code</param> 198 /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
198 /// <returns>Compiled compute shader code</returns> 199 /// <returns>Compiled compute shader code</returns>
199 public CachedShaderProgram GetComputeShader( 200 public CachedShaderProgram GetComputeShader(
200 GpuChannel channel, 201 GpuChannel channel,
202 int samplerPoolMaximumId,
201 GpuChannelPoolState poolState, 203 GpuChannelPoolState poolState,
202 GpuChannelComputeState computeState, 204 GpuChannelComputeState computeState,
203 ulong gpuVa) 205 ulong gpuVa)
@@ -214,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
214 } 216 }
215 217
216 ShaderSpecializationState specState = new(ref computeState); 218 ShaderSpecializationState specState = new(ref computeState);
217 GpuAccessorState gpuAccessorState = new(poolState, computeState, default, specState); 219 GpuAccessorState gpuAccessorState = new(samplerPoolMaximumId, poolState, computeState, default, specState);
218 GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState); 220 GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState);
219 gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false); 221 gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);
220 222
@@ -291,6 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
291 /// <param name="state">GPU state</param> 293 /// <param name="state">GPU state</param>
292 /// <param name="pipeline">Pipeline state</param> 294 /// <param name="pipeline">Pipeline state</param>
293 /// <param name="channel">GPU channel</param> 295 /// <param name="channel">GPU channel</param>
296 /// <param name="samplerPoolMaximumId">Maximum ID that an entry in the sampler pool may have</param>
294 /// <param name="poolState">Texture pool state</param> 297 /// <param name="poolState">Texture pool state</param>
295 /// <param name="graphicsState">3D engine state</param> 298 /// <param name="graphicsState">3D engine state</param>
296 /// <param name="addresses">Addresses of the shaders for each stage</param> 299 /// <param name="addresses">Addresses of the shaders for each stage</param>
@@ -299,6 +302,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
299 ref ThreedClassState state, 302 ref ThreedClassState state,
300 ref ProgramPipelineState pipeline, 303 ref ProgramPipelineState pipeline,
301 GpuChannel channel, 304 GpuChannel channel,
305 int samplerPoolMaximumId,
302 ref GpuChannelPoolState poolState, 306 ref GpuChannelPoolState poolState,
303 ref GpuChannelGraphicsState graphicsState, 307 ref GpuChannelGraphicsState graphicsState,
304 ShaderAddresses addresses) 308 ShaderAddresses addresses)
@@ -319,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
319 UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel); 323 UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel);
320 324
321 ShaderSpecializationState specState = new(ref graphicsState, ref pipeline, transformFeedbackDescriptors); 325 ShaderSpecializationState specState = new(ref graphicsState, ref pipeline, transformFeedbackDescriptors);
322 GpuAccessorState gpuAccessorState = new(poolState, default, graphicsState, specState, transformFeedbackDescriptors); 326 GpuAccessorState gpuAccessorState = new(samplerPoolMaximumId, poolState, default, graphicsState, specState, transformFeedbackDescriptors);
323 327
324 ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan(); 328 ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
325 329
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
index ea8f164f1..ed56db3b3 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
@@ -185,11 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
185 { 185 {
186 if (texture.ArrayLength > 1) 186 if (texture.ArrayLength > 1)
187 { 187 {
188 bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; 188 ResourceType type = GetTextureResourceType(texture, isImage);
189
190 ResourceType type = isBuffer
191 ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
192 : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
193 189
194 _resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages)); 190 _resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages));
195 } 191 }
@@ -242,16 +238,38 @@ namespace Ryujinx.Graphics.Gpu.Shader
242 { 238 {
243 foreach (TextureDescriptor texture in textures) 239 foreach (TextureDescriptor texture in textures)
244 { 240 {
245 bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; 241 ResourceType type = GetTextureResourceType(texture, isImage);
246
247 ResourceType type = isBuffer
248 ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
249 : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
250 242
251 _resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); 243 _resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
252 } 244 }
253 } 245 }
254 246
247 private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage)
248 {
249 bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
250
251 if (isBuffer)
252 {
253 return isImage ? ResourceType.BufferImage : ResourceType.BufferTexture;
254 }
255 else if (isImage)
256 {
257 return ResourceType.Image;
258 }
259 else if (texture.Type == SamplerType.None)
260 {
261 return ResourceType.Sampler;
262 }
263 else if (texture.Separate)
264 {
265 return ResourceType.Texture;
266 }
267 else
268 {
269 return ResourceType.TextureAndSampler;
270 }
271 }
272
255 /// <summary> 273 /// <summary>
256 /// Creates a new shader information structure from the added information. 274 /// Creates a new shader information structure from the added information.
257 /// </summary> 275 /// </summary>
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index c90a0b8f4..98acb6f27 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
31 PrimitiveTopology = 1 << 1, 31 PrimitiveTopology = 1 << 1,
32 TransformFeedback = 1 << 3, 32 TransformFeedback = 1 << 3,
33 TextureArrayFromBuffer = 1 << 4, 33 TextureArrayFromBuffer = 1 << 4,
34 TextureArrayFromPool = 1 << 5,
34 } 35 }
35 36
36 private QueriedStateFlags _queriedState; 37 private QueriedStateFlags _queriedState;
@@ -154,7 +155,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
154 } 155 }
155 156
156 private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization; 157 private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
157 private readonly Dictionary<TextureKey, int> _textureArraySpecialization; 158 private readonly Dictionary<TextureKey, int> _textureArrayFromBufferSpecialization;
159 private readonly Dictionary<bool, int> _textureArrayFromPoolSpecialization;
158 private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures; 160 private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures;
159 private Box<TextureSpecializationState>[][] _textureByBinding; 161 private Box<TextureSpecializationState>[][] _textureByBinding;
160 private Box<TextureSpecializationState>[][] _imageByBinding; 162 private Box<TextureSpecializationState>[][] _imageByBinding;
@@ -165,7 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
165 private ShaderSpecializationState() 167 private ShaderSpecializationState()
166 { 168 {
167 _textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>(); 169 _textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>();
168 _textureArraySpecialization = new Dictionary<TextureKey, int>(); 170 _textureArrayFromBufferSpecialization = new Dictionary<TextureKey, int>();
171 _textureArrayFromPoolSpecialization = new Dictionary<bool, int>();
169 } 172 }
170 173
171 /// <summary> 174 /// <summary>
@@ -327,7 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
327 } 330 }
328 331
329 /// <summary> 332 /// <summary>
330 /// Indicates that the coordinate normalization state of a given texture was used during the shader translation process. 333 /// Registers the length of a texture array calculated from a constant buffer size.
331 /// </summary> 334 /// </summary>
332 /// <param name="stageIndex">Shader stage where the texture is used</param> 335 /// <param name="stageIndex">Shader stage where the texture is used</param>
333 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 336 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
@@ -335,11 +338,22 @@ namespace Ryujinx.Graphics.Gpu.Shader
335 /// <param name="length">Number of elements in the texture array</param> 338 /// <param name="length">Number of elements in the texture array</param>
336 public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length) 339 public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length)
337 { 340 {
338 _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length; 341 _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length;
339 _queriedState |= QueriedStateFlags.TextureArrayFromBuffer; 342 _queriedState |= QueriedStateFlags.TextureArrayFromBuffer;
340 } 343 }
341 344
342 /// <summary> 345 /// <summary>
346 /// Registers the length of a texture array calculated from a texture or sampler pool capacity.
347 /// </summary>
348 /// <param name="isSampler">True for sampler pool, false for texture pool</param>
349 /// <param name="length">Number of elements in the texture array</param>
350 public void RegisterTextureArrayLengthFromPool(bool isSampler, int length)
351 {
352 _textureArrayFromPoolSpecialization[isSampler] = length;
353 _queriedState |= QueriedStateFlags.TextureArrayFromPool;
354 }
355
356 /// <summary>
343 /// Indicates that the format of a given texture was used during the shader translation process. 357 /// Indicates that the format of a given texture was used during the shader translation process.
344 /// </summary> 358 /// </summary>
345 /// <param name="stageIndex">Shader stage where the texture is used</param> 359 /// <param name="stageIndex">Shader stage where the texture is used</param>
@@ -385,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
385 } 399 }
386 400
387 /// <summary> 401 /// <summary>
388 /// Checks if a given texture was registerd on this specialization state. 402 /// Checks if a given texture was registered on this specialization state.
389 /// </summary> 403 /// </summary>
390 /// <param name="stageIndex">Shader stage where the texture is used</param> 404 /// <param name="stageIndex">Shader stage where the texture is used</param>
391 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 405 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
@@ -396,14 +410,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
396 } 410 }
397 411
398 /// <summary> 412 /// <summary>
399 /// Checks if a given texture array (from constant buffer) was registerd on this specialization state. 413 /// Checks if a given texture array (from constant buffer) was registered on this specialization state.
400 /// </summary> 414 /// </summary>
401 /// <param name="stageIndex">Shader stage where the texture is used</param> 415 /// <param name="stageIndex">Shader stage where the texture is used</param>
402 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 416 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
403 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 417 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
418 /// <returns>True if the length for the given buffer and stage exists, false otherwise</returns>
404 public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot) 419 public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot)
405 { 420 {
406 return _textureArraySpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot)); 421 return _textureArrayFromBufferSpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot));
422 }
423
424 /// <summary>
425 /// Checks if a given texture array (from a sampler pool or texture pool) was registered on this specialization state.
426 /// </summary>
427 /// <param name="isSampler">True for sampler pool, false for texture pool</param>
428 /// <returns>True if the length for the given pool, false otherwise</returns>
429 public bool TextureArrayFromPoolRegistered(bool isSampler)
430 {
431 return _textureArrayFromPoolSpecialization.ContainsKey(isSampler);
407 } 432 }
408 433
409 /// <summary> 434 /// <summary>
@@ -412,6 +437,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
412 /// <param name="stageIndex">Shader stage where the texture is used</param> 437 /// <param name="stageIndex">Shader stage where the texture is used</param>
413 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 438 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
414 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 439 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
440 /// <returns>Format and sRGB tuple</returns>
415 public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot) 441 public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot)
416 { 442 {
417 TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value; 443 TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value;
@@ -424,6 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
424 /// <param name="stageIndex">Shader stage where the texture is used</param> 450 /// <param name="stageIndex">Shader stage where the texture is used</param>
425 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 451 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
426 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 452 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
453 /// <returns>Texture target</returns>
427 public TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot) 454 public TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot)
428 { 455 {
429 return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget; 456 return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget;
@@ -435,6 +462,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
435 /// <param name="stageIndex">Shader stage where the texture is used</param> 462 /// <param name="stageIndex">Shader stage where the texture is used</param>
436 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 463 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
437 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 464 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
465 /// <returns>True if coordinates are normalized, false otherwise</returns>
438 public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot) 466 public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot)
439 { 467 {
440 return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized; 468 return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized;
@@ -446,9 +474,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
446 /// <param name="stageIndex">Shader stage where the texture is used</param> 474 /// <param name="stageIndex">Shader stage where the texture is used</param>
447 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 475 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
448 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 476 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
477 /// <returns>Texture array length</returns>
449 public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot) 478 public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot)
450 { 479 {
451 return _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)]; 480 return _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)];
481 }
482
483 /// <summary>
484 /// Gets the recorded length of a given texture array (from a sampler or texture pool).
485 /// </summary>
486 /// <param name="isSampler">True to get the sampler pool length, false to get the texture pool length</param>
487 /// <returns>Texture array length</returns>
488 public int GetTextureArrayFromPoolLength(bool isSampler)
489 {
490 return _textureArrayFromPoolSpecialization[isSampler];
452 } 491 }
453 492
454 /// <summary> 493 /// <summary>
@@ -894,7 +933,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
894 dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); 933 dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
895 dataReader.Read(ref length); 934 dataReader.Read(ref length);
896 935
897 specState._textureArraySpecialization[textureKey] = length; 936 specState._textureArrayFromBufferSpecialization[textureKey] = length;
937 }
938 }
939
940 if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
941 {
942 dataReader.Read(ref count);
943
944 for (int index = 0; index < count; index++)
945 {
946 bool textureKey = default;
947 int length = 0;
948
949 dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
950 dataReader.Read(ref length);
951
952 specState._textureArrayFromPoolSpecialization[textureKey] = length;
898 } 953 }
899 } 954 }
900 955
@@ -965,10 +1020,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
965 1020
966 if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) 1021 if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
967 { 1022 {
968 count = (ushort)_textureArraySpecialization.Count; 1023 count = (ushort)_textureArrayFromBufferSpecialization.Count;
1024 dataWriter.Write(ref count);
1025
1026 foreach (var kv in _textureArrayFromBufferSpecialization)
1027 {
1028 var textureKey = kv.Key;
1029 var length = kv.Value;
1030
1031 dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic);
1032 dataWriter.Write(ref length);
1033 }
1034 }
1035
1036 if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
1037 {
1038 count = (ushort)_textureArrayFromPoolSpecialization.Count;
969 dataWriter.Write(ref count); 1039 dataWriter.Write(ref count);
970 1040
971 foreach (var kv in _textureArraySpecialization) 1041 foreach (var kv in _textureArrayFromPoolSpecialization)
972 { 1042 {
973 var textureKey = kv.Key; 1043 var textureKey = kv.Key;
974 var length = kv.Value; 1044 var length = kv.Value;
diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
index a945cbf20..d56c40af4 100644
--- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
+++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
@@ -176,6 +176,7 @@ namespace Ryujinx.Graphics.OpenGL
176 supportsCubemapView: true, 176 supportsCubemapView: true,
177 supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, 177 supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
178 supportsScaledVertexFormats: true, 178 supportsScaledVertexFormats: true,
179 supportsSeparateSampler: false,
179 supportsShaderBallot: HwCapabilities.SupportsShaderBallot, 180 supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
180 supportsShaderBarrierDivergence: !(intelWindows || intelUnix), 181 supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
181 supportsShaderFloat64: true, 182 supportsShaderFloat64: true,
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 763487dac..eb6c689b8 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -6,7 +6,6 @@ using System;
6using System.Collections.Generic; 6using System.Collections.Generic;
7using System.Globalization; 7using System.Globalization;
8using System.Linq; 8using System.Linq;
9using System.Numerics;
10 9
11namespace Ryujinx.Graphics.Shader.CodeGen.Glsl 10namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
12{ 11{
@@ -352,7 +351,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
352 arrayDecl = "[]"; 351 arrayDecl = "[]";
353 } 352 }
354 353
355 string samplerTypeName = definition.Type.ToGlslSamplerType(); 354 string samplerTypeName = definition.Separate ? definition.Type.ToGlslTextureType() : definition.Type.ToGlslSamplerType();
356 355
357 string layout = string.Empty; 356 string layout = string.Empty;
358 357
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index b4773b819..f0e57b534 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -639,14 +639,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
639 639
640 private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) 640 private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
641 { 641 {
642 TextureDefinition definition = context.Properties.Textures[texOp.Binding]; 642 TextureDefinition textureDefinition = context.Properties.Textures[texOp.Binding];
643 string name = definition.Name; 643 string name = textureDefinition.Name;
644 644
645 if (definition.ArrayLength != 1) 645 if (textureDefinition.ArrayLength != 1)
646 { 646 {
647 name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; 647 name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
648 } 648 }
649 649
650 if (texOp.IsSeparate)
651 {
652 TextureDefinition samplerDefinition = context.Properties.Textures[texOp.SamplerBinding];
653 string samplerName = samplerDefinition.Name;
654
655 if (samplerDefinition.ArrayLength != 1)
656 {
657 samplerName = $"{samplerName}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
658 }
659
660 name = $"{texOp.Type.ToGlslSamplerType()}({name}, {samplerName})";
661 }
662
650 return name; 663 return name;
651 } 664 }
652 665
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 9633c522e..37df4df80 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -160,37 +160,49 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
160 { 160 {
161 int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0; 161 int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0;
162 162
163 var dim = (sampler.Type & SamplerType.Mask) switch 163 SpvInstruction imageType;
164 SpvInstruction sampledImageType;
165
166 if (sampler.Type != SamplerType.None)
164 { 167 {
165 SamplerType.Texture1D => Dim.Dim1D, 168 var dim = (sampler.Type & SamplerType.Mask) switch
166 SamplerType.Texture2D => Dim.Dim2D, 169 {
167 SamplerType.Texture3D => Dim.Dim3D, 170 SamplerType.Texture1D => Dim.Dim1D,
168 SamplerType.TextureCube => Dim.Cube, 171 SamplerType.Texture2D => Dim.Dim2D,
169 SamplerType.TextureBuffer => Dim.Buffer, 172 SamplerType.Texture3D => Dim.Dim3D,
170 _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."), 173 SamplerType.TextureCube => Dim.Cube,
171 }; 174 SamplerType.TextureBuffer => Dim.Buffer,
175 _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."),
176 };
177
178 imageType = context.TypeImage(
179 context.TypeFP32(),
180 dim,
181 sampler.Type.HasFlag(SamplerType.Shadow),
182 sampler.Type.HasFlag(SamplerType.Array),
183 sampler.Type.HasFlag(SamplerType.Multisample),
184 1,
185 ImageFormat.Unknown);
186
187 sampledImageType = context.TypeSampledImage(imageType);
188 }
189 else
190 {
191 imageType = sampledImageType = context.TypeSampler();
192 }
172 193
173 var imageType = context.TypeImage( 194 var sampledOrSeparateImageType = sampler.Separate ? imageType : sampledImageType;
174 context.TypeFP32(), 195 var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledOrSeparateImageType);
175 dim,
176 sampler.Type.HasFlag(SamplerType.Shadow),
177 sampler.Type.HasFlag(SamplerType.Array),
178 sampler.Type.HasFlag(SamplerType.Multisample),
179 1,
180 ImageFormat.Unknown);
181
182 var sampledImageType = context.TypeSampledImage(imageType);
183 var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
184 var sampledImageArrayPointerType = sampledImagePointerType; 196 var sampledImageArrayPointerType = sampledImagePointerType;
185 197
186 if (sampler.ArrayLength == 0) 198 if (sampler.ArrayLength == 0)
187 { 199 {
188 var sampledImageArrayType = context.TypeRuntimeArray(sampledImageType); 200 var sampledImageArrayType = context.TypeRuntimeArray(sampledOrSeparateImageType);
189 sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType); 201 sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
190 } 202 }
191 else if (sampler.ArrayLength != 1) 203 else if (sampler.ArrayLength != 1)
192 { 204 {
193 var sampledImageArrayType = context.TypeArray(sampledImageType, context.Constant(context.TypeU32(), sampler.ArrayLength)); 205 var sampledImageArrayType = context.TypeArray(sampledOrSeparateImageType, context.Constant(context.TypeU32(), sampler.ArrayLength));
194 sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType); 206 sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
195 } 207 }
196 208
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
index 409e466cd..34f8532a6 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
@@ -838,16 +838,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
838 } 838 }
839 839
840 SamplerDeclaration declaration = context.Samplers[texOp.Binding]; 840 SamplerDeclaration declaration = context.Samplers[texOp.Binding];
841 SpvInstruction image = declaration.Image; 841 SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
842
843 if (declaration.IsIndexed)
844 {
845 SpvInstruction textureIndex = Src(AggregateType.S32);
846
847 image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
848 }
849
850 image = context.Load(declaration.SampledImageType, image);
851 842
852 int pCount = texOp.Type.GetDimensions(); 843 int pCount = texOp.Type.GetDimensions();
853 844
@@ -1171,16 +1162,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
1171 } 1162 }
1172 1163
1173 SamplerDeclaration declaration = context.Samplers[texOp.Binding]; 1164 SamplerDeclaration declaration = context.Samplers[texOp.Binding];
1174 SpvInstruction image = declaration.Image; 1165 SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
1175
1176 if (declaration.IsIndexed)
1177 {
1178 SpvInstruction textureIndex = Src(AggregateType.S32);
1179
1180 image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
1181 }
1182
1183 image = context.Load(declaration.SampledImageType, image);
1184 1166
1185 int coordsCount = texOp.Type.GetDimensions(); 1167 int coordsCount = texOp.Type.GetDimensions();
1186 1168
@@ -1449,17 +1431,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
1449 { 1431 {
1450 AstTextureOperation texOp = (AstTextureOperation)operation; 1432 AstTextureOperation texOp = (AstTextureOperation)operation;
1451 1433
1452 SamplerDeclaration declaration = context.Samplers[texOp.Binding]; 1434 int srcIndex = 0;
1453 SpvInstruction image = declaration.Image;
1454
1455 if (declaration.IsIndexed)
1456 {
1457 SpvInstruction textureIndex = context.GetS32(texOp.GetSource(0));
1458 1435
1459 image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex); 1436 SamplerDeclaration declaration = context.Samplers[texOp.Binding];
1460 } 1437 SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
1461 1438
1462 image = context.Load(declaration.SampledImageType, image);
1463 image = context.Image(declaration.ImageType, image); 1439 image = context.Image(declaration.ImageType, image);
1464 1440
1465 SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image); 1441 SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
@@ -1471,17 +1447,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
1471 { 1447 {
1472 AstTextureOperation texOp = (AstTextureOperation)operation; 1448 AstTextureOperation texOp = (AstTextureOperation)operation;
1473 1449
1474 SamplerDeclaration declaration = context.Samplers[texOp.Binding]; 1450 int srcIndex = 0;
1475 SpvInstruction image = declaration.Image;
1476
1477 if (declaration.IsIndexed)
1478 {
1479 SpvInstruction textureIndex = context.GetS32(texOp.GetSource(0));
1480 1451
1481 image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex); 1452 SamplerDeclaration declaration = context.Samplers[texOp.Binding];
1482 } 1453 SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
1483 1454
1484 image = context.Load(declaration.SampledImageType, image);
1485 image = context.Image(declaration.ImageType, image); 1455 image = context.Image(declaration.ImageType, image);
1486 1456
1487 if (texOp.Index == 3) 1457 if (texOp.Index == 3)
@@ -1506,8 +1476,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
1506 1476
1507 if (hasLod) 1477 if (hasLod)
1508 { 1478 {
1509 int lodSrcIndex = declaration.IsIndexed ? 1 : 0; 1479 var lod = context.GetS32(operation.GetSource(srcIndex));
1510 var lod = context.GetS32(operation.GetSource(lodSrcIndex));
1511 result = context.ImageQuerySizeLod(resultType, image, lod); 1480 result = context.ImageQuerySizeLod(resultType, image, lod);
1512 } 1481 }
1513 else 1482 else
@@ -1905,6 +1874,43 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
1905 } 1874 }
1906 } 1875 }
1907 1876
1877 private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
1878 {
1879 SpvInstruction image = declaration.Image;
1880
1881 if (declaration.IsIndexed)
1882 {
1883 SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
1884
1885 image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
1886 }
1887
1888 if (texOp.IsSeparate)
1889 {
1890 image = context.Load(declaration.ImageType, image);
1891
1892 SamplerDeclaration samplerDeclaration = context.Samplers[texOp.SamplerBinding];
1893
1894 SpvInstruction sampler = samplerDeclaration.Image;
1895
1896 if (samplerDeclaration.IsIndexed)
1897 {
1898 SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
1899
1900 sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
1901 }
1902
1903 sampler = context.Load(samplerDeclaration.ImageType, sampler);
1904 image = context.SampledImage(declaration.SampledImageType, image, sampler);
1905 }
1906 else
1907 {
1908 image = context.Load(declaration.SampledImageType, image);
1909 }
1910
1911 return image;
1912 }
1913
1908 private static OperationResult GenerateUnary( 1914 private static OperationResult GenerateUnary(
1909 CodeGenContext context, 1915 CodeGenContext context,
1910 AstOperation operation, 1916 AstOperation operation,
diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 99366ad67..b1a9f9f84 100644
--- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -27,13 +27,6 @@ namespace Ryujinx.Graphics.Shader
27 ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize); 27 ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
28 28
29 /// <summary> 29 /// <summary>
30 /// Gets the size in bytes of a bound constant buffer for the current shader stage.
31 /// </summary>
32 /// <param name="slot">The number of the constant buffer to get the size from</param>
33 /// <returns>Size in bytes</returns>
34 int QueryTextureArrayLengthFromBuffer(int slot);
35
36 /// <summary>
37 /// Queries the binding number of a constant buffer. 30 /// Queries the binding number of a constant buffer.
38 /// </summary> 31 /// </summary>
39 /// <param name="index">Constant buffer index</param> 32 /// <param name="index">Constant buffer index</param>
@@ -299,6 +292,15 @@ namespace Ryujinx.Graphics.Shader
299 } 292 }
300 293
301 /// <summary> 294 /// <summary>
295 /// Queries host API support for separate textures and samplers.
296 /// </summary>
297 /// <returns>True if the API supports samplers and textures to be combined on the shader, false otherwise</returns>
298 bool QueryHostSupportsSeparateSampler()
299 {
300 return true;
301 }
302
303 /// <summary>
302 /// Queries host GPU shader ballot support. 304 /// Queries host GPU shader ballot support.
303 /// </summary> 305 /// </summary>
304 /// <returns>True if the GPU and driver supports shader ballot, false otherwise</returns> 306 /// <returns>True if the GPU and driver supports shader ballot, false otherwise</returns>
@@ -389,6 +391,12 @@ namespace Ryujinx.Graphics.Shader
389 } 391 }
390 392
391 /// <summary> 393 /// <summary>
394 /// Gets the maximum number of samplers that the bound texture pool may have.
395 /// </summary>
396 /// <returns>Maximum amount of samplers that the pool may have</returns>
397 int QuerySamplerArrayLengthFromPool();
398
399 /// <summary>
392 /// Queries sampler type information. 400 /// Queries sampler type information.
393 /// </summary> 401 /// </summary>
394 /// <param name="handle">Texture handle</param> 402 /// <param name="handle">Texture handle</param>
@@ -400,6 +408,19 @@ namespace Ryujinx.Graphics.Shader
400 } 408 }
401 409
402 /// <summary> 410 /// <summary>
411 /// Gets the size in bytes of a bound constant buffer for the current shader stage.
412 /// </summary>
413 /// <param name="slot">The number of the constant buffer to get the size from</param>
414 /// <returns>Size in bytes</returns>
415 int QueryTextureArrayLengthFromBuffer(int slot);
416
417 /// <summary>
418 /// Gets the maximum number of textures that the bound texture pool may have.
419 /// </summary>
420 /// <returns>Maximum amount of textures that the pool may have</returns>
421 int QueryTextureArrayLengthFromPool();
422
423 /// <summary>
403 /// Queries texture coordinate normalization information. 424 /// Queries texture coordinate normalization information.
404 /// </summary> 425 /// </summary>
405 /// <param name="handle">Texture handle</param> 426 /// <param name="handle">Texture handle</param>
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
index 0c1b2a3f3..713e8a4fb 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
@@ -216,6 +216,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
216 216
217 newSources[index] = source; 217 newSources[index] = source;
218 218
219 if (source != null && source.Type == OperandType.LocalVariable)
220 {
221 source.UseOps.Add(this);
222 }
223
219 _sources = newSources; 224 _sources = newSources;
220 } 225 }
221 226
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
index 1b82e2945..74ec5ca61 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
9 public TextureFlags Flags { get; private set; } 9 public TextureFlags Flags { get; private set; }
10 10
11 public int Binding { get; private set; } 11 public int Binding { get; private set; }
12 public int SamplerBinding { get; private set; }
12 13
13 public TextureOperation( 14 public TextureOperation(
14 Instruction inst, 15 Instruction inst,
@@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
24 Format = format; 25 Format = format;
25 Flags = flags; 26 Flags = flags;
26 Binding = binding; 27 Binding = binding;
28 SamplerBinding = -1;
27 } 29 }
28 30
29 public void TurnIntoArray(int binding) 31 public void TurnIntoArray(int binding)
@@ -32,6 +34,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
32 Binding = binding; 34 Binding = binding;
33 } 35 }
34 36
37 public void TurnIntoArray(int textureBinding, int samplerBinding)
38 {
39 TurnIntoArray(textureBinding);
40
41 SamplerBinding = samplerBinding;
42 }
43
35 public void SetBinding(int binding) 44 public void SetBinding(int binding)
36 { 45 {
37 if ((Flags & TextureFlags.Bindless) != 0) 46 if ((Flags & TextureFlags.Bindless) != 0)
diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs
index 66c748bf3..a693495fa 100644
--- a/src/Ryujinx.Graphics.Shader/SamplerType.cs
+++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs
@@ -69,6 +69,7 @@ namespace Ryujinx.Graphics.Shader
69 { 69 {
70 string typeName = (type & SamplerType.Mask) switch 70 string typeName = (type & SamplerType.Mask) switch
71 { 71 {
72 SamplerType.None => "sampler",
72 SamplerType.Texture1D => "sampler1D", 73 SamplerType.Texture1D => "sampler1D",
73 SamplerType.TextureBuffer => "samplerBuffer", 74 SamplerType.TextureBuffer => "samplerBuffer",
74 SamplerType.Texture2D => "sampler2D", 75 SamplerType.Texture2D => "sampler2D",
@@ -95,6 +96,31 @@ namespace Ryujinx.Graphics.Shader
95 return typeName; 96 return typeName;
96 } 97 }
97 98
99 public static string ToGlslTextureType(this SamplerType type)
100 {
101 string typeName = (type & SamplerType.Mask) switch
102 {
103 SamplerType.Texture1D => "texture1D",
104 SamplerType.TextureBuffer => "textureBuffer",
105 SamplerType.Texture2D => "texture2D",
106 SamplerType.Texture3D => "texture3D",
107 SamplerType.TextureCube => "textureCube",
108 _ => throw new ArgumentException($"Invalid texture type \"{type}\"."),
109 };
110
111 if ((type & SamplerType.Multisample) != 0)
112 {
113 typeName += "MS";
114 }
115
116 if ((type & SamplerType.Array) != 0)
117 {
118 typeName += "Array";
119 }
120
121 return typeName;
122 }
123
98 public static string ToGlslImageType(this SamplerType type, AggregateType componentType) 124 public static string ToGlslImageType(this SamplerType type, AggregateType componentType)
99 { 125 {
100 string typeName = (type & SamplerType.Mask) switch 126 string typeName = (type & SamplerType.Mask) switch
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
index 3970df1e9..4068c4127 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
@@ -9,6 +9,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
9 public TextureFlags Flags { get; } 9 public TextureFlags Flags { get; }
10 10
11 public int Binding { get; } 11 public int Binding { get; }
12 public int SamplerBinding { get; }
13
14 public bool IsSeparate => SamplerBinding >= 0;
12 15
13 public AstTextureOperation( 16 public AstTextureOperation(
14 Instruction inst, 17 Instruction inst,
@@ -16,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
16 TextureFormat format, 19 TextureFormat format,
17 TextureFlags flags, 20 TextureFlags flags,
18 int binding, 21 int binding,
22 int samplerBinding,
19 int index, 23 int index,
20 params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) 24 params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length)
21 { 25 {
@@ -23,6 +27,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
23 Format = format; 27 Format = format;
24 Flags = flags; 28 Flags = flags;
25 Binding = binding; 29 Binding = binding;
30 SamplerBinding = samplerBinding;
26 } 31 }
27 } 32 }
28} 33}
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
index 2e2df7546..c4ebaee73 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
@@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
169 169
170 AstTextureOperation GetAstTextureOperation(TextureOperation texOp) 170 AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
171 { 171 {
172 return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.Index, sources); 172 return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.SamplerBinding, texOp.Index, sources);
173 } 173 }
174 174
175 int componentsCount = BitOperations.PopCount((uint)operation.Index); 175 int componentsCount = BitOperations.PopCount((uint)operation.Index);
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs
index bdd3a2ed1..1021dff0e 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/TextureDefinition.cs
@@ -5,25 +5,39 @@ namespace Ryujinx.Graphics.Shader
5 public int Set { get; } 5 public int Set { get; }
6 public int Binding { get; } 6 public int Binding { get; }
7 public int ArrayLength { get; } 7 public int ArrayLength { get; }
8 public bool Separate { get; }
8 public string Name { get; } 9 public string Name { get; }
9 public SamplerType Type { get; } 10 public SamplerType Type { get; }
10 public TextureFormat Format { get; } 11 public TextureFormat Format { get; }
11 public TextureUsageFlags Flags { get; } 12 public TextureUsageFlags Flags { get; }
12 13
13 public TextureDefinition(int set, int binding, int arrayLength, string name, SamplerType type, TextureFormat format, TextureUsageFlags flags) 14 public TextureDefinition(
15 int set,
16 int binding,
17 int arrayLength,
18 bool separate,
19 string name,
20 SamplerType type,
21 TextureFormat format,
22 TextureUsageFlags flags)
14 { 23 {
15 Set = set; 24 Set = set;
16 Binding = binding; 25 Binding = binding;
17 ArrayLength = arrayLength; 26 ArrayLength = arrayLength;
27 Separate = separate;
18 Name = name; 28 Name = name;
19 Type = type; 29 Type = type;
20 Format = format; 30 Format = format;
21 Flags = flags; 31 Flags = flags;
22 } 32 }
23 33
34 public TextureDefinition(int set, int binding, string name, SamplerType type) : this(set, binding, 1, false, name, type, TextureFormat.Unknown, TextureUsageFlags.None)
35 {
36 }
37
24 public TextureDefinition SetFlag(TextureUsageFlags flag) 38 public TextureDefinition SetFlag(TextureUsageFlags flag)
25 { 39 {
26 return new TextureDefinition(Set, Binding, ArrayLength, Name, Type, Format, Flags | flag); 40 return new TextureDefinition(Set, Binding, ArrayLength, Separate, Name, Type, Format, Flags | flag);
27 } 41 }
28 } 42 }
29} 43}
diff --git a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs
index 38834da72..d287a1aa7 100644
--- a/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs
+++ b/src/Ryujinx.Graphics.Shader/TextureDescriptor.cs
@@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.Shader
13 public readonly int HandleIndex; 13 public readonly int HandleIndex;
14 public readonly int ArrayLength; 14 public readonly int ArrayLength;
15 15
16 public readonly bool Separate;
17
16 public readonly TextureUsageFlags Flags; 18 public readonly TextureUsageFlags Flags;
17 19
18 public TextureDescriptor( 20 public TextureDescriptor(
@@ -22,6 +24,7 @@ namespace Ryujinx.Graphics.Shader
22 int cbufSlot, 24 int cbufSlot,
23 int handleIndex, 25 int handleIndex,
24 int arrayLength, 26 int arrayLength,
27 bool separate,
25 TextureUsageFlags flags) 28 TextureUsageFlags flags)
26 { 29 {
27 Binding = binding; 30 Binding = binding;
@@ -30,6 +33,7 @@ namespace Ryujinx.Graphics.Shader
30 CbufSlot = cbufSlot; 33 CbufSlot = cbufSlot;
31 HandleIndex = handleIndex; 34 HandleIndex = handleIndex;
32 ArrayLength = arrayLength; 35 ArrayLength = arrayLength;
36 Separate = separate;
33 Flags = flags; 37 Flags = flags;
34 } 38 }
35 } 39 }
diff --git a/src/Ryujinx.Graphics.Shader/TextureHandle.cs b/src/Ryujinx.Graphics.Shader/TextureHandle.cs
index 7df9c8e47..3aaceac48 100644
--- a/src/Ryujinx.Graphics.Shader/TextureHandle.cs
+++ b/src/Ryujinx.Graphics.Shader/TextureHandle.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader
9 SeparateSamplerHandle = 1, 9 SeparateSamplerHandle = 1,
10 SeparateSamplerId = 2, 10 SeparateSamplerId = 2,
11 SeparateConstantSamplerHandle = 3, 11 SeparateConstantSamplerHandle = 3,
12 Direct = 4,
12 } 13 }
13 14
14 public static class TextureHandle 15 public static class TextureHandle
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
index ad955278f..223215439 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
@@ -1,6 +1,7 @@
1using Ryujinx.Graphics.Shader.Instructions; 1using Ryujinx.Graphics.Shader.Instructions;
2using Ryujinx.Graphics.Shader.IntermediateRepresentation; 2using Ryujinx.Graphics.Shader.IntermediateRepresentation;
3using Ryujinx.Graphics.Shader.StructuredIr; 3using Ryujinx.Graphics.Shader.StructuredIr;
4using System;
4using System.Collections.Generic; 5using System.Collections.Generic;
5 6
6namespace Ryujinx.Graphics.Shader.Translation.Optimizations 7namespace Ryujinx.Graphics.Shader.Translation.Optimizations
@@ -31,7 +32,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
31 continue; 32 continue;
32 } 33 }
33 34
34 if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp)) 35 if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp) &&
36 !GenerateBindlessAccess(block, resourceManager, gpuAccessor, texOp, node))
35 { 37 {
36 // If we can't do bindless elimination, remove the texture operation. 38 // If we can't do bindless elimination, remove the texture operation.
37 // Set any destination variables to zero. 39 // Set any destination variables to zero.
@@ -46,6 +48,88 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
46 } 48 }
47 } 49 }
48 50
51 private static bool GenerateBindlessAccess(
52 BasicBlock block,
53 ResourceManager resourceManager,
54 IGpuAccessor gpuAccessor,
55 TextureOperation texOp,
56 LinkedListNode<INode> node)
57 {
58 if (!gpuAccessor.QueryHostSupportsSeparateSampler())
59 {
60 // We depend on combining samplers and textures in the shader being supported for this.
61
62 return false;
63 }
64
65 Operand nvHandle = texOp.GetSource(0);
66
67 if (nvHandle.AsgOp is not Operation handleOp ||
68 handleOp.Inst != Instruction.Load ||
69 handleOp.StorageKind != StorageKind.Input)
70 {
71 // Right now, we only allow bindless access when the handle comes from a shader input.
72 // This is an artificial limitation to prevent it from being used in cases where it
73 // would have a large performance impact of loading all textures in the pool.
74 // It might be removed in the future, if we can mitigate the performance impact.
75
76 return false;
77 }
78
79 Operand textureHandle = OperandHelper.Local();
80 Operand samplerHandle = OperandHelper.Local();
81 Operand textureIndex = OperandHelper.Local();
82
83 block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff)));
84 block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20)));
85
86 int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
87
88 block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, textureIndex, textureHandle, OperandHelper.Const(texturePoolLength - 1)));
89
90 texOp.SetSource(0, textureIndex);
91
92 bool hasSampler = !texOp.Inst.IsImage();
93
94 int textureBinding = resourceManager.GetTextureOrImageBinding(
95 texOp.Inst,
96 texOp.Type,
97 texOp.Format,
98 texOp.Flags & ~TextureFlags.Bindless,
99 0,
100 TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
101 texturePoolLength,
102 hasSampler);
103
104 if (hasSampler)
105 {
106 Operand samplerIndex = OperandHelper.Local();
107
108 int samplerPoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QuerySamplerArrayLengthFromPool());
109
110 block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, samplerIndex, samplerHandle, OperandHelper.Const(samplerPoolLength - 1)));
111
112 texOp.InsertSource(1, samplerIndex);
113
114 int samplerBinding = resourceManager.GetTextureOrImageBinding(
115 texOp.Inst,
116 SamplerType.None,
117 texOp.Format,
118 TextureFlags.None,
119 0,
120 TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
121 samplerPoolLength);
122
123 texOp.TurnIntoArray(textureBinding, samplerBinding);
124 }
125 else
126 {
127 texOp.TurnIntoArray(textureBinding);
128 }
129
130 return true;
131 }
132
49 private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp) 133 private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
50 { 134 {
51 if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery()) 135 if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs
index 7543d1c24..f2be7975d 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToArray.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
11 private const int HardcodedArrayLengthOgl = 4; 11 private const int HardcodedArrayLengthOgl = 4;
12 12
13 // 1 and 0 elements are not considered arrays anymore. 13 // 1 and 0 elements are not considered arrays anymore.
14 private const int MinimumArrayLength = 2; 14 public const int MinimumArrayLength = 2;
15 15
16 public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager) 16 public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager)
17 { 17 {
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
index e9fe0b1ee..890501c91 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation
29 29
30 private readonly HashSet<int> _usedConstantBufferBindings; 30 private readonly HashSet<int> _usedConstantBufferBindings;
31 31
32 private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, SamplerType Type, TextureFormat Format); 32 private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, bool Separate, SamplerType Type, TextureFormat Format);
33 33
34 private struct TextureMeta 34 private struct TextureMeta
35 { 35 {
@@ -225,7 +225,8 @@ namespace Ryujinx.Graphics.Shader.Translation
225 TextureFlags flags, 225 TextureFlags flags,
226 int cbufSlot, 226 int cbufSlot,
227 int handle, 227 int handle,
228 int arrayLength = 1) 228 int arrayLength = 1,
229 bool separate = false)
229 { 230 {
230 inst &= Instruction.Mask; 231 inst &= Instruction.Mask;
231 bool isImage = inst.IsImage(); 232 bool isImage = inst.IsImage();
@@ -239,7 +240,18 @@ namespace Ryujinx.Graphics.Shader.Translation
239 format = TextureFormat.Unknown; 240 format = TextureFormat.Unknown;
240 } 241 }
241 242
242 int binding = GetTextureOrImageBinding(cbufSlot, handle, arrayLength, type, format, isImage, intCoords, isWrite, accurateType, coherent); 243 int binding = GetTextureOrImageBinding(
244 cbufSlot,
245 handle,
246 arrayLength,
247 type,
248 format,
249 isImage,
250 intCoords,
251 isWrite,
252 accurateType,
253 coherent,
254 separate);
243 255
244 _gpuAccessor.RegisterTexture(handle, cbufSlot); 256 _gpuAccessor.RegisterTexture(handle, cbufSlot);
245 257
@@ -256,9 +268,10 @@ namespace Ryujinx.Graphics.Shader.Translation
256 bool intCoords, 268 bool intCoords,
257 bool write, 269 bool write,
258 bool accurateType, 270 bool accurateType,
259 bool coherent) 271 bool coherent,
272 bool separate)
260 { 273 {
261 var dimensions = type.GetDimensions(); 274 var dimensions = type == SamplerType.None ? 0 : type.GetDimensions();
262 var dict = isImage ? _usedImages : _usedTextures; 275 var dict = isImage ? _usedImages : _usedTextures;
263 276
264 var usageFlags = TextureUsageFlags.None; 277 var usageFlags = TextureUsageFlags.None;
@@ -290,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.Translation
290 // For array textures, we also want to use type as key, 303 // For array textures, we also want to use type as key,
291 // since we may have texture handles stores in the same buffer, but for textures with different types. 304 // since we may have texture handles stores in the same buffer, but for textures with different types.
292 var keyType = arrayLength > 1 ? type : SamplerType.None; 305 var keyType = arrayLength > 1 ? type : SamplerType.None;
293 var info = new TextureInfo(cbufSlot, handle, arrayLength, keyType, format); 306 var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format);
294 var meta = new TextureMeta() 307 var meta = new TextureMeta()
295 { 308 {
296 AccurateType = accurateType, 309 AccurateType = accurateType,
@@ -332,6 +345,10 @@ namespace Ryujinx.Graphics.Shader.Translation
332 ? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}" 345 ? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}"
333 : $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; 346 : $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
334 } 347 }
348 else if (type == SamplerType.None)
349 {
350 nameSuffix = cbufSlot < 0 ? $"s_tcb_{handle:X}" : $"s_cb{cbufSlot}_{handle:X}";
351 }
335 else 352 else
336 { 353 {
337 nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}"; 354 nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}";
@@ -341,6 +358,7 @@ namespace Ryujinx.Graphics.Shader.Translation
341 isImage ? 3 : 2, 358 isImage ? 3 : 2,
342 binding, 359 binding,
343 arrayLength, 360 arrayLength,
361 separate,
344 $"{_stagePrefix}_{nameSuffix}", 362 $"{_stagePrefix}_{nameSuffix}",
345 meta.Type, 363 meta.Type,
346 info.Format, 364 info.Format,
@@ -495,6 +513,7 @@ namespace Ryujinx.Graphics.Shader.Translation
495 info.CbufSlot, 513 info.CbufSlot,
496 info.Handle, 514 info.Handle,
497 info.ArrayLength, 515 info.ArrayLength,
516 info.Separate,
498 meta.UsageFlags)); 517 meta.UsageFlags));
499 } 518 }
500 519
@@ -514,6 +533,7 @@ namespace Ryujinx.Graphics.Shader.Translation
514 info.CbufSlot, 533 info.CbufSlot,
515 info.Handle, 534 info.Handle,
516 info.ArrayLength, 535 info.ArrayLength,
536 info.Separate,
517 meta.UsageFlags)); 537 meta.UsageFlags));
518 } 538 }
519 } 539 }
diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
index 581f4372c..106535588 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
@@ -413,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation
413 if (Stage == ShaderStage.Vertex) 413 if (Stage == ShaderStage.Vertex)
414 { 414 {
415 int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding; 415 int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding;
416 TextureDefinition indexBuffer = new(2, ibBinding, 1, "ib_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); 416 TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer);
417 resourceManager.Properties.AddOrUpdateTexture(indexBuffer); 417 resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
418 418
419 int inputMap = _program.AttributeUsage.UsedInputAttributes; 419 int inputMap = _program.AttributeUsage.UsedInputAttributes;
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Shader.Translation
422 { 422 {
423 int location = BitOperations.TrailingZeroCount(inputMap); 423 int location = BitOperations.TrailingZeroCount(inputMap);
424 int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location); 424 int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location);
425 TextureDefinition vaBuffer = new(2, binding, 1, $"vb_data{location}", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); 425 TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer);
426 resourceManager.Properties.AddOrUpdateTexture(vaBuffer); 426 resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
427 427
428 inputMap &= ~(1 << location); 428 inputMap &= ~(1 << location);
@@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Shader.Translation
431 else if (Stage == ShaderStage.Geometry) 431 else if (Stage == ShaderStage.Geometry)
432 { 432 {
433 int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding; 433 int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding;
434 TextureDefinition remapBuffer = new(2, trbBinding, 1, "trb_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); 434 TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer);
435 resourceManager.Properties.AddOrUpdateTexture(remapBuffer); 435 resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
436 436
437 int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; 437 int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs
index b9abd8fcd..117f79bb4 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs
@@ -43,11 +43,11 @@ namespace Ryujinx.Graphics.Vulkan
43 int binding = segment.Binding; 43 int binding = segment.Binding;
44 int count = segment.Count; 44 int count = segment.Count;
45 45
46 if (setIndex == PipelineBase.UniformSetIndex) 46 if (IsBufferType(segment.Type))
47 { 47 {
48 entries[seg] = new DescriptorUpdateTemplateEntry() 48 entries[seg] = new DescriptorUpdateTemplateEntry()
49 { 49 {
50 DescriptorType = DescriptorType.UniformBuffer, 50 DescriptorType = segment.Type.Convert(),
51 DstBinding = (uint)binding, 51 DstBinding = (uint)binding,
52 DescriptorCount = (uint)count, 52 DescriptorCount = (uint)count,
53 Offset = structureOffset, 53 Offset = structureOffset,
@@ -56,76 +56,31 @@ namespace Ryujinx.Graphics.Vulkan
56 56
57 structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count); 57 structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
58 } 58 }
59 else if (setIndex == PipelineBase.StorageSetIndex) 59 else if (IsBufferTextureType(segment.Type))
60 { 60 {
61 entries[seg] = new DescriptorUpdateTemplateEntry() 61 entries[seg] = new DescriptorUpdateTemplateEntry()
62 { 62 {
63 DescriptorType = DescriptorType.StorageBuffer, 63 DescriptorType = segment.Type.Convert(),
64 DstBinding = (uint)binding, 64 DstBinding = (uint)binding,
65 DescriptorCount = (uint)count, 65 DescriptorCount = (uint)count,
66 Offset = structureOffset, 66 Offset = structureOffset,
67 Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>() 67 Stride = (nuint)Unsafe.SizeOf<BufferView>()
68 }; 68 };
69 69
70 structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count); 70 structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
71 }
72 else if (setIndex == PipelineBase.TextureSetIndex)
73 {
74 if (segment.Type != ResourceType.BufferTexture)
75 {
76 entries[seg] = new DescriptorUpdateTemplateEntry()
77 {
78 DescriptorType = DescriptorType.CombinedImageSampler,
79 DstBinding = (uint)binding,
80 DescriptorCount = (uint)count,
81 Offset = structureOffset,
82 Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
83 };
84
85 structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
86 }
87 else
88 {
89 entries[seg] = new DescriptorUpdateTemplateEntry()
90 {
91 DescriptorType = DescriptorType.UniformTexelBuffer,
92 DstBinding = (uint)binding,
93 DescriptorCount = (uint)count,
94 Offset = structureOffset,
95 Stride = (nuint)Unsafe.SizeOf<BufferView>()
96 };
97
98 structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
99 }
100 } 71 }
101 else if (setIndex == PipelineBase.ImageSetIndex) 72 else
102 { 73 {
103 if (segment.Type != ResourceType.BufferImage) 74 entries[seg] = new DescriptorUpdateTemplateEntry()
104 {
105 entries[seg] = new DescriptorUpdateTemplateEntry()
106 {
107 DescriptorType = DescriptorType.StorageImage,
108 DstBinding = (uint)binding,
109 DescriptorCount = (uint)count,
110 Offset = structureOffset,
111 Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
112 };
113
114 structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
115 }
116 else
117 { 75 {
118 entries[seg] = new DescriptorUpdateTemplateEntry() 76 DescriptorType = segment.Type.Convert(),
119 { 77 DstBinding = (uint)binding,
120 DescriptorType = DescriptorType.StorageTexelBuffer, 78 DescriptorCount = (uint)count,
121 DstBinding = (uint)binding, 79 Offset = structureOffset,
122 DescriptorCount = (uint)count, 80 Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
123 Offset = structureOffset, 81 };
124 Stride = (nuint)Unsafe.SizeOf<BufferView>() 82
125 }; 83 structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
126
127 structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
128 }
129 } 84 }
130 } 85 }
131 86
@@ -237,6 +192,16 @@ namespace Ryujinx.Graphics.Vulkan
237 Template = result; 192 Template = result;
238 } 193 }
239 194
195 private static bool IsBufferType(ResourceType type)
196 {
197 return type == ResourceType.UniformBuffer || type == ResourceType.StorageBuffer;
198 }
199
200 private static bool IsBufferTextureType(ResourceType type)
201 {
202 return type == ResourceType.BufferTexture || type == ResourceType.BufferImage;
203 }
204
240 public unsafe void Dispose() 205 public unsafe void Dispose()
241 { 206 {
242 _gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null); 207 _gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index e75e7f4b4..b46ba9c46 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -706,6 +706,7 @@ namespace Ryujinx.Graphics.Vulkan
706 supportsCubemapView: !IsAmdGcn, 706 supportsCubemapView: !IsAmdGcn,
707 supportsNonConstantTextureOffset: false, 707 supportsNonConstantTextureOffset: false,
708 supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(), 708 supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
709 supportsSeparateSampler: true,
709 supportsShaderBallot: false, 710 supportsShaderBallot: false,
710 supportsShaderBarrierDivergence: Vendor != Vendor.Intel, 711 supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
711 supportsShaderFloat64: Capabilities.SupportsShaderFloat64, 712 supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
diff --git a/src/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs
index 4252f1b25..d2c6bd59e 100644
--- a/src/Ryujinx.ShaderTools/Program.cs
+++ b/src/Ryujinx.ShaderTools/Program.cs
@@ -58,10 +58,20 @@ namespace Ryujinx.ShaderTools
58 return MemoryMarshal.Cast<byte, ulong>(new ReadOnlySpan<byte>(_data)[(int)address..]); 58 return MemoryMarshal.Cast<byte, ulong>(new ReadOnlySpan<byte>(_data)[(int)address..]);
59 } 59 }
60 60
61 public int QuerySamplerArrayLengthFromPool()
62 {
63 return DefaultArrayLength;
64 }
65
61 public int QueryTextureArrayLengthFromBuffer(int slot) 66 public int QueryTextureArrayLengthFromBuffer(int slot)
62 { 67 {
63 return DefaultArrayLength; 68 return DefaultArrayLength;
64 } 69 }
70
71 public int QueryTextureArrayLengthFromPool()
72 {
73 return DefaultArrayLength;
74 }
65 } 75 }
66 76
67 private class Options 77 private class Options