From c6f8bfed904e30f7c5d890a2f0ef531eb9e298e5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 22 Apr 2024 15:05:55 -0300 Subject: Add support for bindless textures from shader input (vertex buffer) on Vulkan (#6577) * 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 --- .../Shader/CachedShaderBindings.cs | 5 +- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 35 ++++++++ .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 18 +++++ src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs | 2 + .../Shader/GpuAccessorState.cs | 8 ++ .../Shader/GpuChannelPoolState.cs | 7 +- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 8 +- .../Shader/ShaderInfoBuilder.cs | 38 ++++++--- .../Shader/ShaderSpecializationState.cs | 92 +++++++++++++++++++--- 10 files changed, 187 insertions(+), 28 deletions(-) (limited to 'src/Ryujinx.Graphics.Gpu/Shader') diff --git a/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/src/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs index 6e36753e..a80dcbc8 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 TextureBindings[i] = stage.Info.Textures.Select(descriptor => { - Target target = ShaderTexture.GetTarget(descriptor.Type); + Target target = descriptor.Type != SamplerType.None ? ShaderTexture.GetTarget(descriptor.Type) : default; var result = new TextureBindingInfo( target, @@ -66,7 +66,8 @@ namespace Ryujinx.Graphics.Gpu.Shader descriptor.ArrayLength, descriptor.CbufSlot, descriptor.HandleIndex, - descriptor.Flags); + descriptor.Flags, + descriptor.Type == SamplerType.None); if (descriptor.ArrayLength <= 1) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 681838a9..45f32e2d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -109,6 +109,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; } + /// + /// Pool length is not available on the cache + public int QuerySamplerArrayLengthFromPool() + { + return QueryArrayLengthFromPool(isSampler: true); + } + /// public SamplerType QuerySamplerType(int handle, int cbufSlot) { @@ -117,6 +124,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } /// + /// Constant buffer derived length is not available on the cache public int QueryTextureArrayLengthFromBuffer(int slot) { if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot)) @@ -130,6 +138,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return arrayLength; } + /// + /// Pool length is not available on the cache + public int QueryTextureArrayLengthFromPool() + { + return QueryArrayLengthFromPool(isSampler: false); + } + /// public TextureFormat QueryTextureFormat(int handle, int cbufSlot) { @@ -170,6 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache } /// + /// Texture information is not available on the cache public void RegisterTexture(int handle, int cbufSlot) { if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot)) @@ -182,5 +198,24 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized); } + + /// + /// Gets the cached texture or sampler pool capacity. + /// + /// True to get sampler pool length, false for texture pool length + /// Pool length + /// Pool length is not available on the cache + private int QueryArrayLengthFromPool(bool isSampler) + { + if (!_oldSpecState.TextureArrayFromPoolRegistered(isSampler)) + { + throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength); + } + + int arrayLength = _oldSpecState.GetTextureArrayFromPoolLength(isSampler); + _newSpecState.RegisterTextureArrayLengthFromPool(isSampler, arrayLength); + + return arrayLength; + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b6a277a2..2c19cc4b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 6489; + private const uint CodeGenVersion = 6577; private const string SharedTocFileName = "shared.toc"; 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 1d22ab93..04949690 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -120,6 +120,15 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; } + /// + public int QuerySamplerArrayLengthFromPool() + { + int length = _state.SamplerPoolMaximumId + 1; + _state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: true, length); + + return length; + } + /// public SamplerType QuerySamplerType(int handle, int cbufSlot) { @@ -141,6 +150,15 @@ namespace Ryujinx.Graphics.Gpu.Shader return arrayLength; } + /// + public int QueryTextureArrayLengthFromPool() + { + int length = _state.PoolState.TexturePoolMaximumId + 1; + _state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: false, length); + + return length; + } + //// public TextureFormat QueryTextureFormat(int handle, int cbufSlot) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 06e5edf1..0d562b0d 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 public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats; + public bool QueryHostSupportsSeparateSampler() => _context.Capabilities.SupportsSeparateSampler; + public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; 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 cfc4a2cc..808bf185 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs @@ -5,6 +5,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// class GpuAccessorState { + /// + /// Maximum ID that a sampler pool entry may have. + /// + public readonly int SamplerPoolMaximumId; + /// /// GPU texture pool state. /// @@ -38,18 +43,21 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Creates a new GPU accessor state. /// + /// Maximum ID that a sampler pool entry may have /// GPU texture pool state /// GPU compute state, for compute shaders /// GPU graphics state, for vertex, tessellation, geometry and fragment shaders /// Shader specialization state (shared by all stages) /// Transform feedback information, if the shader uses transform feedback. Otherwise, should be null public GpuAccessorState( + int samplerPoolMaximumId, GpuChannelPoolState poolState, GpuChannelComputeState computeState, GpuChannelGraphicsState graphicsState, ShaderSpecializationState specializationState, TransformFeedbackDescriptor[] transformFeedbackDescriptors = null) { + SamplerPoolMaximumId = samplerPoolMaximumId; PoolState = poolState; GraphicsState = graphicsState; ComputeState = computeState; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs index ddb45152..a2ab9933 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs @@ -2,7 +2,6 @@ using System; namespace Ryujinx.Graphics.Gpu.Shader { -#pragma warning disable CS0659 // Class overrides Object.Equals(object o) but does not override Object.GetHashCode() /// /// State used by the . /// @@ -52,6 +51,10 @@ namespace Ryujinx.Graphics.Gpu.Shader { return obj is GpuChannelPoolState state && Equals(state); } + + public override int GetHashCode() + { + return HashCode.Combine(TexturePoolGpuVa, TexturePoolMaximumId, TextureBufferIndex); + } } -#pragma warning restore CS0659 } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 0b17af8b..31cc94a2 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 /// This automatically translates, compiles and adds the code to the cache if not present. /// /// GPU channel + /// Maximum ID that an entry in the sampler pool may have /// Texture pool state /// Compute engine state /// GPU virtual address of the binary shader code /// Compiled compute shader code public CachedShaderProgram GetComputeShader( GpuChannel channel, + int samplerPoolMaximumId, GpuChannelPoolState poolState, GpuChannelComputeState computeState, ulong gpuVa) @@ -214,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } ShaderSpecializationState specState = new(ref computeState); - GpuAccessorState gpuAccessorState = new(poolState, computeState, default, specState); + GpuAccessorState gpuAccessorState = new(samplerPoolMaximumId, poolState, computeState, default, specState); GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState); gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false); @@ -291,6 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU state /// Pipeline state /// GPU channel + /// Maximum ID that an entry in the sampler pool may have /// Texture pool state /// 3D engine state /// Addresses of the shaders for each stage @@ -299,6 +302,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ref ThreedClassState state, ref ProgramPipelineState pipeline, GpuChannel channel, + int samplerPoolMaximumId, ref GpuChannelPoolState poolState, ref GpuChannelGraphicsState graphicsState, ShaderAddresses addresses) @@ -319,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Shader UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel); ShaderSpecializationState specState = new(ref graphicsState, ref pipeline, transformFeedbackDescriptors); - GpuAccessorState gpuAccessorState = new(poolState, default, graphicsState, specState, transformFeedbackDescriptors); + GpuAccessorState gpuAccessorState = new(samplerPoolMaximumId, poolState, default, graphicsState, specState, transformFeedbackDescriptors); ReadOnlySpan addressesSpan = addresses.AsSpan(); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index ea8f164f..ed56db3b 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 { if (texture.ArrayLength > 1) { - bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; - - ResourceType type = isBuffer - ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture) - : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler); + ResourceType type = GetTextureResourceType(texture, isImage); _resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages)); } @@ -242,16 +238,38 @@ namespace Ryujinx.Graphics.Gpu.Shader { foreach (TextureDescriptor texture in textures) { - bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; - - ResourceType type = isBuffer - ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture) - : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler); + ResourceType type = GetTextureResourceType(texture, isImage); _resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); } } + private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage) + { + bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; + + if (isBuffer) + { + return isImage ? ResourceType.BufferImage : ResourceType.BufferTexture; + } + else if (isImage) + { + return ResourceType.Image; + } + else if (texture.Type == SamplerType.None) + { + return ResourceType.Sampler; + } + else if (texture.Separate) + { + return ResourceType.Texture; + } + else + { + return ResourceType.TextureAndSampler; + } + } + /// /// Creates a new shader information structure from the added information. /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index c90a0b8f..98acb6f2 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 PrimitiveTopology = 1 << 1, TransformFeedback = 1 << 3, TextureArrayFromBuffer = 1 << 4, + TextureArrayFromPool = 1 << 5, } private QueriedStateFlags _queriedState; @@ -154,7 +155,8 @@ namespace Ryujinx.Graphics.Gpu.Shader } private readonly Dictionary> _textureSpecialization; - private readonly Dictionary _textureArraySpecialization; + private readonly Dictionary _textureArrayFromBufferSpecialization; + private readonly Dictionary _textureArrayFromPoolSpecialization; private KeyValuePair>[] _allTextures; private Box[][] _textureByBinding; private Box[][] _imageByBinding; @@ -165,7 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader private ShaderSpecializationState() { _textureSpecialization = new Dictionary>(); - _textureArraySpecialization = new Dictionary(); + _textureArrayFromBufferSpecialization = new Dictionary(); + _textureArrayFromPoolSpecialization = new Dictionary(); } /// @@ -327,7 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } /// - /// Indicates that the coordinate normalization state of a given texture was used during the shader translation process. + /// Registers the length of a texture array calculated from a constant buffer size. /// /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer @@ -335,10 +338,21 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Number of elements in the texture array public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length) { - _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length; + _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length; _queriedState |= QueriedStateFlags.TextureArrayFromBuffer; } + /// + /// Registers the length of a texture array calculated from a texture or sampler pool capacity. + /// + /// True for sampler pool, false for texture pool + /// Number of elements in the texture array + public void RegisterTextureArrayLengthFromPool(bool isSampler, int length) + { + _textureArrayFromPoolSpecialization[isSampler] = length; + _queriedState |= QueriedStateFlags.TextureArrayFromPool; + } + /// /// Indicates that the format of a given texture was used during the shader translation process. /// @@ -385,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } /// - /// Checks if a given texture was registerd on this specialization state. + /// Checks if a given texture was registered on this specialization state. /// /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer @@ -396,14 +410,25 @@ namespace Ryujinx.Graphics.Gpu.Shader } /// - /// Checks if a given texture array (from constant buffer) was registerd on this specialization state. + /// Checks if a given texture array (from constant buffer) was registered on this specialization state. /// /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer /// Slot of the texture buffer constant buffer + /// True if the length for the given buffer and stage exists, false otherwise public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot) { - return _textureArraySpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot)); + return _textureArrayFromBufferSpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot)); + } + + /// + /// Checks if a given texture array (from a sampler pool or texture pool) was registered on this specialization state. + /// + /// True for sampler pool, false for texture pool + /// True if the length for the given pool, false otherwise + public bool TextureArrayFromPoolRegistered(bool isSampler) + { + return _textureArrayFromPoolSpecialization.ContainsKey(isSampler); } /// @@ -412,6 +437,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer /// Slot of the texture buffer constant buffer + /// Format and sRGB tuple public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot) { TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value; @@ -424,6 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer /// Slot of the texture buffer constant buffer + /// Texture target public TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot) { return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget; @@ -435,6 +462,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer /// Slot of the texture buffer constant buffer + /// True if coordinates are normalized, false otherwise public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot) { return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized; @@ -446,9 +474,20 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader stage where the texture is used /// Offset in words of the texture handle on the texture buffer /// Slot of the texture buffer constant buffer + /// Texture array length public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot) { - return _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)]; + return _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)]; + } + + /// + /// Gets the recorded length of a given texture array (from a sampler or texture pool). + /// + /// True to get the sampler pool length, false to get the texture pool length + /// Texture array length + public int GetTextureArrayFromPoolLength(bool isSampler) + { + return _textureArrayFromPoolSpecialization[isSampler]; } /// @@ -894,7 +933,23 @@ namespace Ryujinx.Graphics.Gpu.Shader dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); dataReader.Read(ref length); - specState._textureArraySpecialization[textureKey] = length; + specState._textureArrayFromBufferSpecialization[textureKey] = length; + } + } + + if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool)) + { + dataReader.Read(ref count); + + for (int index = 0; index < count; index++) + { + bool textureKey = default; + int length = 0; + + dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); + dataReader.Read(ref length); + + specState._textureArrayFromPoolSpecialization[textureKey] = length; } } @@ -965,10 +1020,25 @@ namespace Ryujinx.Graphics.Gpu.Shader if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) { - count = (ushort)_textureArraySpecialization.Count; + count = (ushort)_textureArrayFromBufferSpecialization.Count; + dataWriter.Write(ref count); + + foreach (var kv in _textureArrayFromBufferSpecialization) + { + var textureKey = kv.Key; + var length = kv.Value; + + dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic); + dataWriter.Write(ref length); + } + } + + if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool)) + { + count = (ushort)_textureArrayFromPoolSpecialization.Count; dataWriter.Write(ref count); - foreach (var kv in _textureArraySpecialization) + foreach (var kv in _textureArrayFromPoolSpecialization) { var textureKey = kv.Key; var length = kv.Value; -- cgit v1.2.3