diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Pool.cs | 42 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Sampler.cs | 7 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/SamplerPool.cs | 35 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Texture.cs | 8 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs | 290 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 56 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 92 |
7 files changed, 430 insertions, 100 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index f54ce1d7..8e210513 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -1,6 +1,7 @@ using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.Gpu.Memory; using System; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Image { @@ -16,6 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image protected GpuContext Context; protected PhysicalMemory PhysicalMemory; protected int SequenceNumber; + protected int ModifiedSequenceNumber; protected T1[] Items; protected T2[] DescriptorCache; @@ -41,6 +43,9 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly CpuMultiRegionHandle _memoryTracking; private readonly Action<ulong, ulong> _modifiedDelegate; + private int _modifiedSequenceOffset; + private bool _modified; + /// <summary> /// Creates a new instance of the GPU resource pool. /// </summary> @@ -80,6 +85,16 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> + /// Gets a reference to the descriptor for a given ID. + /// </summary> + /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param> + /// <returns>A reference to the descriptor</returns> + public ref readonly T2 GetDescriptorRef(int id) + { + return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(Address + (ulong)id * DescriptorSize, DescriptorSize))[0]; + } + + /// <summary> /// Gets the GPU resource with the given ID. /// </summary> /// <param name="id">ID of the resource. This is effectively a zero-based index</param> @@ -93,7 +108,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public void SynchronizeMemory() { + _modified = false; _memoryTracking.QueryModified(_modifiedDelegate); + + if (_modified) + { + UpdateModifiedSequence(); + } } /// <summary> @@ -103,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="mSize">Size of the modified region</param> private void RegionModified(ulong mAddress, ulong mSize) { + _modified = true; + if (mAddress < Address) { mAddress = Address; @@ -119,6 +142,15 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> + /// Updates the modified sequence number using the current sequence number and offset, + /// indicating that it has been modified. + /// </summary> + protected void UpdateModifiedSequence() + { + ModifiedSequenceNumber = SequenceNumber + _modifiedSequenceOffset; + } + + /// <summary> /// An action to be performed when a precise memory access occurs to this resource. /// Makes sure that the dirty flags are checked. /// </summary> @@ -129,6 +161,16 @@ namespace Ryujinx.Graphics.Gpu.Image { if (write && Context.SequenceNumber == SequenceNumber) { + if (ModifiedSequenceNumber == SequenceNumber + _modifiedSequenceOffset) + { + // The modified sequence number is offset when PreciseActions occur so that + // users checking it will see an increment and know the pool has changed since + // their last look, even though the main SequenceNumber has not been changed. + + _modifiedSequenceOffset++; + } + + // Force the pool to be checked again the next time it is used. SequenceNumber--; } diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/Ryujinx.Graphics.Gpu/Image/Sampler.cs index f8923d34..b70ac9eb 100644 --- a/Ryujinx.Graphics.Gpu/Image/Sampler.cs +++ b/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -9,6 +9,11 @@ namespace Ryujinx.Graphics.Gpu.Image class Sampler : IDisposable { /// <summary> + /// True if the sampler is disposed, false otherwise. + /// </summary> + public bool IsDisposed { get; private set; } + + /// <summary> /// Host sampler object. /// </summary> private readonly ISampler _hostSampler; @@ -101,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public void Dispose() { + IsDisposed = true; + _hostSampler.Dispose(); _anisoSampler?.Dispose(); } diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs index e205ec48..e95800ad 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -48,6 +48,8 @@ namespace Ryujinx.Graphics.Gpu.Image Items[i] = null; } } + + UpdateModifiedSequence(); } SequenceNumber = Context.SequenceNumber; @@ -72,6 +74,39 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> + /// Checks if the pool was modified, and returns the last sequence number where a modification was detected. + /// </summary> + /// <returns>A number that increments each time a modification is detected</returns> + public int CheckModified() + { + if (SequenceNumber != Context.SequenceNumber) + { + SequenceNumber = Context.SequenceNumber; + + if (_forcedAnisotropy != GraphicsConfig.MaxAnisotropy) + { + _forcedAnisotropy = GraphicsConfig.MaxAnisotropy; + + for (int i = 0; i < Items.Length; i++) + { + if (Items[i] != null) + { + Items[i].Dispose(); + + Items[i] = null; + } + } + + UpdateModifiedSequence(); + } + + SynchronizeMemory(); + } + + return ModifiedSequenceNumber; + } + + /// <summary> /// Implementation of the sampler pool range invalidation. /// </summary> /// <param name="address">Start address of the range of the sampler pool</param> diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index aadb4260..cb10f456 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -100,6 +100,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public bool AlwaysFlushOnOverlap { get; private set; } + /// <summary> + /// Increments when the host texture is swapped, or when the texture is removed from all pools. + /// </summary> + 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++; } /// <summary> @@ -1535,6 +1541,8 @@ namespace Ryujinx.Graphics.Gpu.Image _poolOwners.Clear(); } + + InvalidatedSequence++; } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 7ac4e12e..f15f8885 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -1,8 +1,12 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; +using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Shader; using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Image { @@ -35,17 +39,24 @@ namespace Ryujinx.Graphics.Gpu.Image { public ITexture Texture; public ISampler Sampler; + + public int TextureHandle; + public int SamplerHandle; + public int InvalidatedSequence; + public Texture CachedTexture; + public Sampler CachedSampler; } - private readonly TextureStatePerStage[][] _textureState; - private readonly TextureStatePerStage[][] _imageState; + private TextureStatePerStage[] _textureState; + private TextureStatePerStage[] _imageState; private int[] _textureBindingsCount; private int[] _imageBindingsCount; - private int _textureBufferIndex; + private int _texturePoolSequence; + private int _samplerPoolSequence; - private bool _rebind; + private int _textureBufferIndex; private readonly float[] _scales; private bool _scaleChanged; @@ -72,8 +83,8 @@ namespace Ryujinx.Graphics.Gpu.Image _textureBindings = new TextureBindingInfo[stages][]; _imageBindings = new TextureBindingInfo[stages][]; - _textureState = new TextureStatePerStage[stages][]; - _imageState = new TextureStatePerStage[stages][]; + _textureState = new TextureStatePerStage[InitialTextureStateSize]; + _imageState = new TextureStatePerStage[InitialImageStateSize]; _textureBindingsCount = new int[stages]; _imageBindingsCount = new int[stages]; @@ -82,9 +93,6 @@ namespace Ryujinx.Graphics.Gpu.Image { _textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize]; _imageBindings[stage] = new TextureBindingInfo[InitialImageStateSize]; - - _textureState[stage] = new TextureStatePerStage[InitialTextureStateSize]; - _imageState[stage] = new TextureStatePerStage[InitialImageStateSize]; } } @@ -99,15 +107,6 @@ namespace Ryujinx.Graphics.Gpu.Image if (count > _textureBindings[stage].Length) { Array.Resize(ref _textureBindings[stage], count); - Array.Resize(ref _textureState[stage], count); - } - - int toClear = Math.Max(_textureBindingsCount[stage], count); - TextureStatePerStage[] state = _textureState[stage]; - - for (int i = 0; i < toClear; i++) - { - state[i] = new TextureStatePerStage(); } _textureBindingsCount[stage] = count; @@ -126,20 +125,29 @@ namespace Ryujinx.Graphics.Gpu.Image if (count > _imageBindings[stage].Length) { Array.Resize(ref _imageBindings[stage], count); - Array.Resize(ref _imageState[stage], count); } - int toClear = Math.Max(_imageBindingsCount[stage], count); - TextureStatePerStage[] state = _imageState[stage]; + _imageBindingsCount[stage] = count; + + return _imageBindings[stage]; + } - for (int i = 0; i < toClear; i++) + /// <summary> + /// Sets the max binding indexes for textures and images. + /// </summary> + /// <param name="maxTextureBinding">The maximum texture binding</param> + /// <param name="maxImageBinding">The maximum image binding</param> + public void SetMaxBindings(int maxTextureBinding, int maxImageBinding) + { + if (maxTextureBinding >= _textureState.Length) { - state[i] = new TextureStatePerStage(); + Array.Resize(ref _textureState, maxTextureBinding + 1); } - _imageBindingsCount[stage] = count; - - return _imageBindings[stage]; + if (maxImageBinding >= _imageState.Length) + { + Array.Resize(ref _imageState, maxImageBinding + 1); + } } /// <summary> @@ -323,7 +331,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// Ensures that the bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> - public void CommitBindings() + /// <param name="specState">Specialization state for the bound shader</param> + /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns> + public bool CommitBindings(ShaderSpecializationState specState) { ulong texturePoolAddress = _texturePoolAddress; @@ -331,10 +341,38 @@ namespace Ryujinx.Graphics.Gpu.Image ? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId) : null; + // Check if the texture pool has been modified since bindings were last committed. + // If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same. + bool poolModified = false; + + if (texturePool != null) + { + int texturePoolSequence = texturePool.CheckModified(); + + if (_texturePoolSequence != texturePoolSequence) + { + poolModified = true; + _texturePoolSequence = texturePoolSequence; + } + } + + if (_samplerPool != null) + { + int samplerPoolSequence = _samplerPool.CheckModified(); + + if (_samplerPoolSequence != samplerPoolSequence) + { + poolModified = true; + _samplerPoolSequence = samplerPoolSequence; + } + } + + bool specStateMatches = true; + if (_isCompute) { - CommitTextureBindings(texturePool, ShaderStage.Compute, 0); - CommitImageBindings (texturePool, ShaderStage.Compute, 0); + specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState); + specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState); } else { @@ -342,14 +380,57 @@ namespace Ryujinx.Graphics.Gpu.Image { int stageIndex = (int)stage - 1; - CommitTextureBindings(texturePool, stage, stageIndex); - CommitImageBindings (texturePool, stage, stageIndex); + specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState); + specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState); } } CommitRenderScale(); - _rebind = false; + return specStateMatches; + } + + /// <summary> + /// Fetch the constant buffers used for a texture to cache. + /// </summary> + /// <param name="stageIndex">Stage index of the constant buffer</param> + /// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param> + /// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param> + /// <param name="cachedTextureBuffer">The currently cached texture buffer data</param> + /// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param> + /// <param name="textureBufferIndex">The new texture buffer index</param> + /// <param name="samplerBufferIndex">The new sampler buffer index</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateCachedBuffer( + int stageIndex, + ref int cachedTextureBufferIndex, + ref int cachedSamplerBufferIndex, + ref ReadOnlySpan<int> cachedTextureBuffer, + ref ReadOnlySpan<int> cachedSamplerBuffer, + int textureBufferIndex, + int samplerBufferIndex) + { + if (textureBufferIndex != cachedTextureBufferIndex) + { + ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex); + + cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size)); + cachedTextureBufferIndex = textureBufferIndex; + + if (samplerBufferIndex == textureBufferIndex) + { + cachedSamplerBuffer = cachedTextureBuffer; + cachedSamplerBufferIndex = samplerBufferIndex; + } + } + + if (samplerBufferIndex != cachedSamplerBufferIndex) + { + ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex); + + cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size)); + cachedSamplerBufferIndex = samplerBufferIndex; + } } /// <summary> @@ -358,13 +439,16 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> /// <param name="pool">The current texture pool</param> /// <param name="stage">The shader stage using the textures to be bound</param> - /// <param name="stageIndex">The stage number of the specified shader stage</param> - private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex) + /// <param name="stageIndex">The stage number of the specified shader stage</param + /// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param> + /// <param name="specState">Specialization state for the bound shader</param> + /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns> + private bool CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState) { int textureCount = _textureBindingsCount[stageIndex]; if (textureCount == 0) { - return; + return true; } var samplerPool = _samplerPool; @@ -372,17 +456,26 @@ namespace Ryujinx.Graphics.Gpu.Image if (pool == null) { Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set."); - return; + return true; } + bool specStateMatches = true; + + int cachedTextureBufferIndex = -1; + int cachedSamplerBufferIndex = -1; + ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty; + ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty; + for (int index = 0; index < textureCount; index++) { TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index]; (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex); - int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex); - int textureId = UnpackTextureId(packedId); + UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex); + + int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer); + int textureId = TextureHandle.UnpackTextureId(packedId); int samplerId; if (_samplerIndex == SamplerIndex.ViaHeaderIndex) @@ -391,10 +484,30 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - samplerId = UnpackSamplerId(packedId); + samplerId = TextureHandle.UnpackSamplerId(packedId); } - Texture texture = pool.Get(textureId); + ref TextureStatePerStage state = ref _textureState[bindingInfo.Binding]; + + if (!poolModified && + state.TextureHandle == textureId && + state.SamplerHandle == samplerId && + state.CachedTexture != null && + state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence && + state.CachedSampler?.IsDisposed != true) + { + // The texture is already bound. + state.CachedTexture.SynchronizeMemory(); + + continue; + } + + state.TextureHandle = textureId; + state.SamplerHandle = samplerId; + + ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture); + + specStateMatches &= specState.MatchesTexture(stage, index, descriptor); ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); @@ -407,30 +520,36 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - if (_textureState[stageIndex][index].Texture != hostTexture || _rebind) + if (state.Texture != hostTexture) { if (UpdateScale(texture, bindingInfo, index, stage)) { hostTexture = texture?.GetTargetTexture(bindingInfo.Target); } - _textureState[stageIndex][index].Texture = hostTexture; + state.Texture = hostTexture; _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture); } Sampler sampler = samplerPool?.Get(samplerId); + state.CachedSampler = sampler; ISampler hostSampler = sampler?.GetHostSampler(texture); - if (_textureState[stageIndex][index].Sampler != hostSampler || _rebind) + if (state.Sampler != hostSampler) { - _textureState[stageIndex][index].Sampler = hostSampler; + state.Sampler = hostSampler; _context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler); } + + state.CachedTexture = texture; + state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0; } } + + return specStateMatches; } /// <summary> @@ -440,38 +559,72 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="pool">The current texture pool</param> /// <param name="stage">The shader stage using the textures to be bound</param> /// <param name="stageIndex">The stage number of the specified shader stage</param> - private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex) + /// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param> + /// <param name="specState">Specialization state for the bound shader</param> + /// <returns>True if all bound images match the current shader specialiation state, false otherwise</returns> + private bool CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState) { int imageCount = _imageBindingsCount[stageIndex]; if (imageCount == 0) { - return; + return true; } if (pool == null) { Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set."); - return; + return true; } // Scales for images appear after the texture ones. int baseScaleIndex = _textureBindingsCount[stageIndex]; + int cachedTextureBufferIndex = -1; + int cachedSamplerBufferIndex = -1; + ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty; + ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty; + + bool specStateMatches = true; + for (int index = 0; index < imageCount; index++) { TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index]; (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex); - int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex); - int textureId = UnpackTextureId(packedId); + UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex); - Texture texture = pool.Get(textureId); + int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer); + int textureId = TextureHandle.UnpackTextureId(packedId); - ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); + ref TextureStatePerStage state = ref _imageState[bindingInfo.Binding]; bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); + if (!poolModified && + state.TextureHandle == textureId && + state.CachedTexture != null && + state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence) + { + // The texture is already bound. + state.CachedTexture.SynchronizeMemory(); + + if (isStore) + { + state.CachedTexture?.SignalModified(); + } + + continue; + } + + state.TextureHandle = textureId; + + ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture); + + specStateMatches &= specState.MatchesImage(stage, index, descriptor); + + ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); + if (hostTexture != null && texture.Target == Target.TextureBuffer) { // Ensure that the buffer texture is using the correct buffer as storage. @@ -494,14 +647,14 @@ namespace Ryujinx.Graphics.Gpu.Image texture?.SignalModified(); } - if (_imageState[stageIndex][index].Texture != hostTexture || _rebind) + if (state.Texture != hostTexture) { if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage)) { hostTexture = texture?.GetTargetTexture(bindingInfo.Target); } - _imageState[stageIndex][index].Texture = hostTexture; + state.Texture = hostTexture; Format format = bindingInfo.Format; @@ -512,8 +665,13 @@ namespace Ryujinx.Graphics.Gpu.Image _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format); } + + state.CachedTexture = texture; + state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0; } } + + return specStateMatches; } /// <summary> @@ -537,7 +695,7 @@ namespace Ryujinx.Graphics.Gpu.Image (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(cbufSlot, bufferIndex); int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex); - int textureId = UnpackTextureId(packedId); + int textureId = TextureHandle.UnpackTextureId(packedId); ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); @@ -555,6 +713,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="textureBufferIndex">Index of the constant buffer holding the texture handles</param> /// <param name="samplerBufferIndex">Index of the constant buffer holding the sampler handles</param> /// <returns>The packed texture and sampler ID (the real texture handle)</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex) { (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset); @@ -591,31 +750,12 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> - /// Unpacks the texture ID from the real texture handle. - /// </summary> - /// <param name="packedId">The real texture handle</param> - /// <returns>The texture ID</returns> - private static int UnpackTextureId(int packedId) - { - return (packedId >> 0) & 0xfffff; - } - - /// <summary> - /// Unpacks the sampler ID from the real texture handle. - /// </summary> - /// <param name="packedId">The real texture handle</param> - /// <returns>The sampler ID</returns> - private static int UnpackSamplerId(int packedId) - { - return (packedId >> 20) & 0xfff; - } - - /// <summary> /// Force all bound textures and images to be rebound the next time CommitBindings is called. /// </summary> public void Rebind() { - _rebind = true; + Array.Clear(_textureState); + Array.Clear(_imageState); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index a1c29291..628c3159 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; +using Ryujinx.Graphics.Gpu.Shader; using System; namespace Ryujinx.Graphics.Gpu.Image @@ -10,9 +11,11 @@ namespace Ryujinx.Graphics.Gpu.Image class TextureManager : IDisposable { private readonly GpuContext _context; + private readonly GpuChannel _channel; private readonly TextureBindingsManager _cpBindingsManager; private readonly TextureBindingsManager _gpBindingsManager; + private readonly TexturePoolCache _texturePoolCache; private readonly Texture[] _rtColors; private readonly ITexture[] _rtHostColors; @@ -35,6 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Image public TextureManager(GpuContext context, GpuChannel channel) { _context = context; + _channel = channel; TexturePoolCache texturePoolCache = new TexturePoolCache(context); @@ -43,6 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Image _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true); _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false); + _texturePoolCache = texturePoolCache; _rtColors = new Texture[Constants.TotalRenderTargets]; _rtHostColors = new ITexture[Constants.TotalRenderTargets]; @@ -100,6 +105,16 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> + /// Sets the max binding indexes on the compute pipeline. + /// </summary> + /// <param name="maxTextureBinding">The maximum texture binding</param> + /// <param name="maxImageBinding">The maximum image binding</param> + public void SetComputeMaxBindings(int maxTextureBinding, int maxImageBinding) + { + _cpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding); + } + + /// <summary> /// Sets the texture constant buffer index on the graphics pipeline. /// </summary> /// <param name="index">The texture constant buffer index</param> @@ -109,6 +124,16 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> + /// Sets the max binding indexes on the graphics pipeline. + /// </summary> + /// <param name="maxTextureBinding">The maximum texture binding</param> + /// <param name="maxImageBinding">The maximum image binding</param> + public void SetGraphicsMaxBindings(int maxTextureBinding, int maxImageBinding) + { + _gpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding); + } + + /// <summary> /// Sets the current sampler pool on the compute pipeline. /// </summary> /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param> @@ -335,25 +360,48 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// Commits bindings on the compute pipeline. /// </summary> - public void CommitComputeBindings() + /// <param name="specState">Specialization state for the bound shader</param> + /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns> + public bool CommitComputeBindings(ShaderSpecializationState specState) { // Every time we switch between graphics and compute work, // we must rebind everything. // Since compute work happens less often, we always do that // before and after the compute dispatch. _cpBindingsManager.Rebind(); - _cpBindingsManager.CommitBindings(); + bool result = _cpBindingsManager.CommitBindings(specState); _gpBindingsManager.Rebind(); + + return result; } /// <summary> /// Commits bindings on the graphics pipeline. /// </summary> - public void CommitGraphicsBindings() + /// <param name="specState">Specialization state for the bound shader</param> + /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns> + public bool CommitGraphicsBindings(ShaderSpecializationState specState) { - _gpBindingsManager.CommitBindings(); + bool result = _gpBindingsManager.CommitBindings(specState); UpdateRenderTargets(); + + return result; + } + + /// <summary> + /// Returns a texture pool from the cache, with the given address and maximum id. + /// </summary> + /// <param name="poolGpuVa">GPU virtual address of the texture pool</param> + /// <param name="maximumId">Maximum ID of the texture pool</param> + /// <returns>The texture pool</returns> + public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId) + { + ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); + + TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId); + + return texturePool; } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 10a6ff82..75974c43 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image { private readonly GpuChannel _channel; private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>(); + private TextureDescriptor _defaultDescriptor; /// <summary> /// Intrusive linked list node used on the texture pool cache. @@ -33,30 +34,19 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> - /// Gets the texture with the given ID. + /// Gets the texture descripor and texture with the given ID with no bounds check or synchronization. /// </summary> /// <param name="id">ID of the texture. This is effectively a zero-based index</param> - /// <returns>The texture with the given ID</returns> - public override Texture Get(int id) + /// <param name="texture">The texture with the given ID</param> + /// <returns>The texture descriptor with the given ID</returns> + private ref readonly TextureDescriptor GetInternal(int id, out Texture texture) { - if ((uint)id >= Items.Length) - { - return null; - } - - if (SequenceNumber != Context.SequenceNumber) - { - SequenceNumber = Context.SequenceNumber; - - SynchronizeMemory(); - } + texture = Items[id]; - Texture texture = Items[id]; + ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(id); if (texture == null) { - TextureDescriptor descriptor = GetDescriptor(id); - TextureInfo info = GetInfo(descriptor, out int layerSize); ProcessDereferenceQueue(); @@ -66,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Image // If this happens, then the texture address is invalid, we can't add it to the cache. if (texture == null) { - return null; + return ref descriptor; } texture.IncrementReferenceCount(this, id); @@ -82,8 +72,6 @@ namespace Ryujinx.Graphics.Gpu.Image // Texture changed size at one point - it may be a different size than the sampler expects. // This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before. - TextureDescriptor descriptor = GetDescriptor(id); - int baseLevel = descriptor.UnpackBaseLevel(); int width = Math.Max(1, descriptor.UnpackWidth() >> baseLevel); int height = Math.Max(1, descriptor.UnpackHeight() >> baseLevel); @@ -98,10 +86,72 @@ namespace Ryujinx.Graphics.Gpu.Image texture.SynchronizeMemory(); } + return ref descriptor; + } + + /// <summary> + /// Gets the texture with the given ID. + /// </summary> + /// <param name="id">ID of the texture. This is effectively a zero-based index</param> + /// <returns>The texture with the given ID</returns> + public override Texture Get(int id) + { + if ((uint)id >= Items.Length) + { + return null; + } + + if (SequenceNumber != Context.SequenceNumber) + { + SequenceNumber = Context.SequenceNumber; + + SynchronizeMemory(); + } + + GetInternal(id, out Texture texture); + return texture; } /// <summary> + /// Gets the texture descriptor and texture with the given ID. + /// </summary> + /// <remarks> + /// This method assumes that the pool has been manually synchronized before doing binding. + /// </remarks> + /// <param name="id">ID of the texture. This is effectively a zero-based index</param> + /// <param name="texture">The texture with the given ID</param> + /// <returns>The texture descriptor with the given ID</returns> + public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture) + { + if ((uint)id >= Items.Length) + { + texture = null; + return ref _defaultDescriptor; + } + + // When getting for binding, assume the pool has already been synchronized. + + return ref GetInternal(id, out texture); + } + + /// <summary> + /// Checks if the pool was modified, and returns the last sequence number where a modification was detected. + /// </summary> + /// <returns>A number that increments each time a modification is detected</returns> + public int CheckModified() + { + if (SequenceNumber != Context.SequenceNumber) + { + SequenceNumber = Context.SequenceNumber; + + SynchronizeMemory(); + } + + return ModifiedSequenceNumber; + } + + /// <summary> /// Forcibly remove a texture from this pool's items. /// If deferred, the dereference will be queued to occur on the render thread. /// </summary> @@ -175,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="descriptor">The texture descriptor</param> /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param> /// <returns>The texture information</returns> - private TextureInfo GetInfo(TextureDescriptor descriptor, out int layerSize) + private TextureInfo GetInfo(in TextureDescriptor descriptor, out int layerSize) { int depthOrLayers = descriptor.UnpackDepth(); int levels = descriptor.UnpackLevels(); |