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; + } } }