diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
index 87c14da8f..f0de882b0 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
@@ -188,6 +188,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
_channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
_channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
+ int maxTextureBinding = 0;
+ int maxImageBinding = 0;
+
TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count);
for (int index = 0; index < info.Textures.Count; index++)
@@ -202,6 +205,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Flags);
+
+ if (descriptor.Binding > maxTextureBinding)
+ {
+ maxTextureBinding = descriptor.Binding;
+ }
}
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count);
@@ -220,9 +228,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Flags);
+
+ if (descriptor.Binding > maxImageBinding)
+ {
+ maxImageBinding = descriptor.Binding;
+ }
}
- _channel.TextureManager.CommitComputeBindings();
+ _channel.TextureManager.SetComputeMaxBindings(maxTextureBinding, maxImageBinding);
+
+ _channel.TextureManager.CommitComputeBindings(cs.SpecializationState); // Should be up to date, since the shader was fetched above.
+
_channel.BufferManager.CommitComputeBindings();
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/Ryujinx.Graphics.Gpu/Image/Sampler.cs
index f8923d349..b70ac9eb9 100644
--- a/Ryujinx.Graphics.Gpu/Image/Sampler.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Sampler.cs
@@ -8,6 +8,11 @@ namespace Ryujinx.Graphics.Gpu.Image
///
class Sampler : IDisposable
{
+ ///
+ /// True if the sampler is disposed, false otherwise.
+ ///
+ public bool IsDisposed { get; private set; }
+
///
/// Host sampler object.
///
@@ -101,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Image
///
public void Dispose()
{
+ IsDisposed = true;
+
_hostSampler.Dispose();
_anisoSampler?.Dispose();
}
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index aadb4260b..cb10f456b 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -100,6 +100,11 @@ namespace Ryujinx.Graphics.Gpu.Image
///
public bool AlwaysFlushOnOverlap { get; private set; }
+ ///
+ /// Increments when the host texture is swapped, or when the texture is removed from all pools.
+ ///
+ public int InvalidatedSequence { get; private set; }
+
private int _depth;
private int _layers;
public int FirstLayer { get; private set; }
@@ -1407,6 +1412,7 @@ namespace Ryujinx.Graphics.Gpu.Image
DisposeTextures();
HostTexture = hostTexture;
+ InvalidatedSequence++;
}
///
@@ -1535,6 +1541,8 @@ namespace Ryujinx.Graphics.Gpu.Image
_poolOwners.Clear();
}
+
+ InvalidatedSequence++;
}
///
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 71f202aed..8472305ef 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -378,6 +378,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
return _gpUniformBuffers[stage].Buffers[index].Address;
}
+ ///
+ /// Gets the bounds of the compute uniform buffer currently bound at the given index.
+ ///
+ /// Indicates whenever the uniform is requested by the 3D or compute engine
+ /// Index of the shader stage, if the uniform is for the 3D engine
+ /// Index of the uniform buffer binding
+ /// The uniform buffer bounds, or an undefined value if the buffer is not currently bound
+ public ref BufferBounds GetUniformBufferBounds(bool isCompute, int stage, int index)
+ {
+ if (isCompute)
+ {
+ return ref _cpUniformBuffers.Buffers[index];
+ }
+ else
+ {
+ return ref _gpUniformBuffers[stage].Buffers[index];
+ }
+ }
+
///
/// Ensures that the compute engine bindings are visible to the host GPU.
/// Note: this actually performs the binding using the host graphics API.
diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs b/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs
index 3b4c65f3d..69fcb2780 100644
--- a/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs
@@ -35,6 +35,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
HostProgram = hostProgram;
SpecializationState = specializationState;
Shaders = shaders;
+
+ SpecializationState.Prepare(shaders);
}
///
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index df4b9d128..0779bf2ce 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -418,7 +418,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
{
- return cpShader.SpecializationState.MatchesCompute(channel, poolState);
+ return cpShader.SpecializationState.MatchesCompute(channel, poolState, true);
}
return false;
@@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
}
- return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState);
+ return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true);
}
///
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs
index e3e57d745..43ccd892c 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
foreach (var entry in _entries)
{
- if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState))
+ if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true))
{
program = entry;
return true;
@@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
foreach (var entry in _entries)
{
- if (entry.SpecializationState.MatchesCompute(channel, poolState))
+ if (entry.SpecializationState.MatchesCompute(channel, poolState, true))
{
program = entry;
return true;
diff --git a/Ryujinx.Graphics.Shader/TextureHandle.cs b/Ryujinx.Graphics.Shader/TextureHandle.cs
index b3712e6bf..83b2dbe8d 100644
--- a/Ryujinx.Graphics.Shader/TextureHandle.cs
+++ b/Ryujinx.Graphics.Shader/TextureHandle.cs
@@ -1,3 +1,4 @@
+using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Shader
@@ -50,5 +51,47 @@ namespace Ryujinx.Graphics.Shader
{
return (handle & 0x3fff, (handle >> 14) & 0x3fff, (TextureHandleType)((uint)handle >> 28));
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int UnpackTextureId(int packedId)
+ {
+ return (packedId >> 0) & 0xfffff;
+ }
+
+ ///
+ /// Reads a packed texture and sampler ID (basically, the real texture handle)
+ /// from a given texture/sampler constant buffer.
+ ///
+ /// A word offset of the handle on the buffer (the "fake" shader handle)
+ /// The constant buffer to fetch texture IDs from
+ /// The constant buffer to fetch sampler IDs from
+ /// The packed texture and sampler ID (the real texture handle)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ReadPackedId(int wordOffset, ReadOnlySpan cachedTextureBuffer, ReadOnlySpan cachedSamplerBuffer)
+ {
+ (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset);
+
+ int handle = cachedTextureBuffer[textureWordOffset];
+
+ // The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
+ // is a 13-bit value. However, in order to also support separate samplers and textures (which uses
+ // bindless textures on the shader), we extend it with another value on the higher 16 bits with
+ // another offset for the sampler.
+ // The shader translator has code to detect separate texture and sampler uses with a bindless texture,
+ // turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
+ if (handleType != TextureHandleType.CombinedSampler)
+ {
+ int samplerHandle = cachedSamplerBuffer[samplerWordOffset];
+
+ if (handleType == TextureHandleType.SeparateSamplerId)
+ {
+ samplerHandle <<= 20;
+ }
+
+ handle |= samplerHandle;
+ }
+
+ return handle;
+ }
}
}