From 4965681e069eeedc5272030b131c2a45e6131e61 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 4 Dec 2022 17:18:40 +0000 Subject: [PATCH] GPU: Swap bindings array instead of copying (#4003) * GPU: Swap bindings array instead of copying Reduces work on UpdateShaderState. Now the cost is a few reference moves for arrays, rather than copying data. Downside: bindings arrays are no longer readonly. * Micro optimisation * Add missing docs * Address Feedback --- .../Engine/Compute/ComputeClass.cs | 52 +-------- .../Engine/Threed/StateUpdater.cs | 80 ++------------ .../Image/TextureBindingsManager.cs | 62 +++-------- Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 60 ++-------- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 51 ++++----- .../Shader/CachedShaderBindings.cs | 103 ++++++++++++++++++ .../Shader/CachedShaderProgram.cs | 6 + 7 files changed, 162 insertions(+), 252 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index cd509471e9..2ac738fdfb 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -202,57 +202,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); } - _channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers); - _channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers); + _channel.BufferManager.SetComputeBufferBindings(cs.Bindings); - int maxTextureBinding = -1; - int maxImageBinding = -1; - - TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count); - - for (int index = 0; index < info.Textures.Count; index++) - { - var descriptor = info.Textures[index]; - - Target target = ShaderTexture.GetTarget(descriptor.Type); - - textureBindings[index] = new TextureBindingInfo( - target, - descriptor.Binding, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Flags); - - if (descriptor.Binding > maxTextureBinding) - { - maxTextureBinding = descriptor.Binding; - } - } - - TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count); - - for (int index = 0; index < info.Images.Count; index++) - { - var descriptor = info.Images[index]; - - Target target = ShaderTexture.GetTarget(descriptor.Type); - Format format = ShaderTexture.GetFormat(descriptor.Format); - - imageBindings[index] = new TextureBindingInfo( - target, - format, - descriptor.Binding, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Flags); - - if (descriptor.Binding > maxImageBinding) - { - maxImageBinding = descriptor.Binding; - } - } - - _channel.TextureManager.SetComputeMaxBindings(maxTextureBinding, maxImageBinding); + _channel.TextureManager.SetComputeBindings(cs.Bindings); // Should never return false for mismatching spec state, since the shader was fetched above. _channel.TextureManager.CommitComputeBindings(cs.SpecializationState); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 8da5ea5ef1..fe7e0d09f0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1257,88 +1257,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed UpdateUserClipState(); } + UpdateShaderBindings(gs.Bindings); + for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { - UpdateStageBindings(stageIndex, gs.Shaders[stageIndex + 1]?.Info); + _currentProgramInfo[stageIndex] = gs.Shaders[stageIndex + 1]?.Info; } _context.Renderer.Pipeline.SetProgram(gs.HostProgram); } /// - /// Updates bindings consumed by the shader stage on the texture and buffer managers. + /// Updates bindings consumed by the shader on the texture and buffer managers. /// - /// Shader stage to have the bindings updated - /// Shader stage bindings info - private void UpdateStageBindings(int stage, ShaderProgramInfo info) + /// Bindings for the active shader + private void UpdateShaderBindings(CachedShaderBindings bindings) { - _currentProgramInfo[stage] = info; - - if (info == null) - { - _channel.TextureManager.RentGraphicsTextureBindings(stage, 0); - _channel.TextureManager.RentGraphicsImageBindings(stage, 0); - _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null); - _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null); - return; - } - - int maxTextureBinding = -1; - int maxImageBinding = -1; - - Span textureBindings = _channel.TextureManager.RentGraphicsTextureBindings(stage, info.Textures.Count); - - if (info.UsesRtLayer) - { - _vtgWritesRtLayer = true; - } - - for (int index = 0; index < info.Textures.Count; index++) - { - var descriptor = info.Textures[index]; - - Target target = ShaderTexture.GetTarget(descriptor.Type); - - textureBindings[index] = new TextureBindingInfo( - target, - descriptor.Binding, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Flags); - - if (descriptor.Binding > maxTextureBinding) - { - maxTextureBinding = descriptor.Binding; - } - } - - TextureBindingInfo[] imageBindings = _channel.TextureManager.RentGraphicsImageBindings(stage, info.Images.Count); - - for (int index = 0; index < info.Images.Count; index++) - { - var descriptor = info.Images[index]; - - Target target = ShaderTexture.GetTarget(descriptor.Type); - Format format = ShaderTexture.GetFormat(descriptor.Format); - - imageBindings[index] = new TextureBindingInfo( - target, - format, - descriptor.Binding, - descriptor.CbufSlot, - descriptor.HandleIndex, - descriptor.Flags); - - if (descriptor.Binding > maxImageBinding) - { - maxImageBinding = descriptor.Binding; - } - } - - _channel.TextureManager.SetGraphicsMaxBindings(maxTextureBinding, maxImageBinding); - - _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); - _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); + _channel.TextureManager.SetGraphicsBindings(bindings); + _channel.BufferManager.SetGraphicsBufferBindings(bindings); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 892d9f6a78..0787ce3d50 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -37,8 +37,8 @@ namespace Ryujinx.Graphics.Gpu.Image private TexturePool _cachedTexturePool; private SamplerPool _cachedSamplerPool; - private readonly TextureBindingInfo[][] _textureBindings; - private readonly TextureBindingInfo[][] _imageBindings; + private TextureBindingInfo[][] _textureBindings; + private TextureBindingInfo[][] _imageBindings; private struct TextureState { @@ -56,9 +56,6 @@ namespace Ryujinx.Graphics.Gpu.Image private TextureState[] _textureState; private TextureState[] _imageState; - private int[] _textureBindingsCount; - private int[] _imageBindingsCount; - private int _texturePoolSequence; private int _samplerPoolSequence; @@ -101,9 +98,6 @@ namespace Ryujinx.Graphics.Gpu.Image _textureState = new TextureState[InitialTextureStateSize]; _imageState = new TextureState[InitialImageStateSize]; - _textureBindingsCount = new int[stages]; - _imageBindingsCount = new int[stages]; - for (int stage = 0; stage < stages; stage++) { _textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize]; @@ -112,39 +106,15 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Rents the texture bindings array for a given stage, so that they can be modified. + /// Sets the texture and image bindings. /// - /// Shader stage number, or 0 for compute shaders - /// The number of bindings needed - /// The texture bindings array - public TextureBindingInfo[] RentTextureBindings(int stage, int count) + /// Bindings for the active shader + public void SetBindings(CachedShaderBindings bindings) { - if (count > _textureBindings[stage].Length) - { - Array.Resize(ref _textureBindings[stage], count); - } + _textureBindings = bindings.TextureBindings; + _imageBindings = bindings.ImageBindings; - _textureBindingsCount[stage] = count; - - return _textureBindings[stage]; - } - - /// - /// Rents the image bindings array for a given stage, so that they can be modified. - /// - /// Shader stage number, or 0 for compute shaders - /// The number of bindings needed - /// The image bindings array - public TextureBindingInfo[] RentImageBindings(int stage, int count) - { - if (count > _imageBindings[stage].Length) - { - Array.Resize(ref _imageBindings[stage], count); - } - - _imageBindingsCount[stage] = count; - - return _imageBindings[stage]; + SetMaxBindings(bindings.MaxTextureBinding, bindings.MaxImageBinding); } /// @@ -257,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Image case ShaderStage.Vertex: int fragmentIndex = (int)ShaderStage.Fragment - 1; - index += _textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex]; + index += _textureBindings[fragmentIndex].Length + _imageBindings[fragmentIndex].Length; result = texture.ScaleFactor; break; @@ -284,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// private bool VertexRequiresScale() { - for (int i = 0; i < _textureBindingsCount[0]; i++) + for (int i = 0; i < _textureBindings[0].Length; i++) { if ((_textureBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0) { @@ -292,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Image } } - for (int i = 0; i < _imageBindingsCount[0]; i++) + for (int i = 0; i < _imageBindings[0].Length; i++) { if ((_imageBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0) { @@ -309,10 +279,10 @@ namespace Ryujinx.Graphics.Gpu.Image private void CommitRenderScale() { // Stage 0 total: Compute or Vertex. - int total = _textureBindingsCount[0] + _imageBindingsCount[0]; + int total = _textureBindings[0].Length + _imageBindings[0].Length; int fragmentIndex = (int)ShaderStage.Fragment - 1; - int fragmentTotal = _isCompute ? 0 : (_textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex]); + int fragmentTotal = _isCompute ? 0 : (_textureBindings[fragmentIndex].Length + _imageBindings[fragmentIndex].Length); if (total != 0 && fragmentTotal != _lastFragmentTotal && VertexRequiresScale()) { @@ -481,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Image bool poolModified, ShaderSpecializationState specState) { - int textureCount = _textureBindingsCount[stageIndex]; + int textureCount = _textureBindings[stageIndex].Length; if (textureCount == 0) { return true; @@ -609,7 +579,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if all bound images match the current shader specialiation state, false otherwise private bool CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState) { - int imageCount = _imageBindingsCount[stageIndex]; + int imageCount = _imageBindings[stageIndex].Length; if (imageCount == 0) { return true; @@ -622,7 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Image } // Scales for images appear after the texture ones. - int baseScaleIndex = _textureBindingsCount[stageIndex]; + int baseScaleIndex = _textureBindings[stageIndex].Length; int cachedTextureBufferIndex = -1; int cachedSamplerBufferIndex = -1; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index fe0175a6a6..083de64c55 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -57,45 +57,21 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Rents the texture bindings array of the compute pipeline. + /// Sets the texture and image bindings for the compute pipeline. /// - /// The number of bindings needed - /// The texture bindings array - public TextureBindingInfo[] RentComputeTextureBindings(int count) + /// Bindings for the active shader + public void SetComputeBindings(CachedShaderBindings bindings) { - return _cpBindingsManager.RentTextureBindings(0, count); + _cpBindingsManager.SetBindings(bindings); } /// - /// Rents the texture bindings array for a given stage on the graphics pipeline. + /// Sets the texture and image bindings for the graphics pipeline. /// - /// The index of the shader stage to bind the textures - /// The number of bindings needed - /// The texture bindings array - public TextureBindingInfo[] RentGraphicsTextureBindings(int stage, int count) + /// Bindings for the active shader + public void SetGraphicsBindings(CachedShaderBindings bindings) { - return _gpBindingsManager.RentTextureBindings(stage, count); - } - - /// - /// Rents the image bindings array of the compute pipeline. - /// - /// The number of bindings needed - /// The image bindings array - public TextureBindingInfo[] RentComputeImageBindings(int count) - { - return _cpBindingsManager.RentImageBindings(0, count); - } - - /// - /// Rents the image bindings array for a given stage on the graphics pipeline. - /// - /// The index of the shader stage to bind the images - /// The number of bindings needed - /// The image bindings array - public TextureBindingInfo[] RentGraphicsImageBindings(int stage, int count) - { - return _gpBindingsManager.RentImageBindings(stage, count); + _gpBindingsManager.SetBindings(bindings); } /// @@ -107,16 +83,6 @@ namespace Ryujinx.Graphics.Gpu.Image _cpBindingsManager.SetTextureBufferIndex(index); } - /// - /// Sets the max binding indexes on the compute pipeline. - /// - /// The maximum texture binding - /// The maximum image binding - public void SetComputeMaxBindings(int maxTextureBinding, int maxImageBinding) - { - _cpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding); - } - /// /// Sets the texture constant buffer index on the graphics pipeline. /// @@ -126,16 +92,6 @@ namespace Ryujinx.Graphics.Gpu.Image _gpBindingsManager.SetTextureBufferIndex(index); } - /// - /// Sets the max binding indexes on the graphics pipeline. - /// - /// The maximum texture binding - /// The maximum image binding - public void SetGraphicsMaxBindings(int maxTextureBinding, int maxImageBinding) - { - _gpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding); - } - /// /// Sets the current sampler pool on the compute pipeline. /// diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index f0831e1582..1728cdb582 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -1,10 +1,10 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Shader; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Runtime.CompilerServices; namespace Ryujinx.Graphics.Gpu.Memory @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Shader buffer binding information. /// - public BufferDescriptor[] Bindings { get; } + public BufferDescriptor[] Bindings { get; private set; } /// /// Buffer regions. @@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Sets shader buffer binding information. /// /// Buffer binding information - public void SetBindings(ReadOnlyCollection descriptors) + public void SetBindings(BufferDescriptor[] descriptors) { if (descriptors == null) { @@ -86,8 +86,10 @@ namespace Ryujinx.Graphics.Gpu.Memory return; } - descriptors.CopyTo(Bindings, 0); - Count = descriptors.Count; + if ((Count = descriptors.Length) != 0) + { + Bindings = descriptors; + } } } @@ -320,41 +322,26 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Sets the binding points for the storage buffers bound on the compute pipeline. /// - /// Buffer descriptors with the binding point values - public void SetComputeStorageBufferBindings(ReadOnlyCollection descriptors) + /// Bindings for the active shader + public void SetComputeBufferBindings(CachedShaderBindings bindings) { - _cpStorageBuffers.SetBindings(descriptors); + _cpStorageBuffers.SetBindings(bindings.StorageBufferBindings[0]); + _cpUniformBuffers.SetBindings(bindings.ConstantBufferBindings[0]); } /// /// Sets the binding points for the storage buffers bound on the graphics pipeline. /// - /// Index of the shader stage - /// Buffer descriptors with the binding point values - public void SetGraphicsStorageBufferBindings(int stage, ReadOnlyCollection descriptors) + /// Bindings for the active shader + public void SetGraphicsBufferBindings(CachedShaderBindings bindings) { - _gpStorageBuffers[stage].SetBindings(descriptors); + for (int i = 0; i < Constants.ShaderStages; i++) + { + _gpStorageBuffers[i].SetBindings(bindings.StorageBufferBindings[i]); + _gpUniformBuffers[i].SetBindings(bindings.ConstantBufferBindings[i]); + } + _gpStorageBuffersDirty = true; - } - - /// - /// Sets the binding points for the uniform buffers bound on the compute pipeline. - /// - /// Buffer descriptors with the binding point values - public void SetComputeUniformBufferBindings(ReadOnlyCollection descriptors) - { - _cpUniformBuffers.SetBindings(descriptors); - } - - /// - /// Sets the enabled uniform buffers mask on the graphics pipeline. - /// Each bit set on the mask indicates that the respective buffer index is enabled. - /// - /// Index of the shader stage - /// Buffer descriptors with the binding point values - public void SetGraphicsUniformBufferBindings(int stage, ReadOnlyCollection descriptors) - { - _gpUniformBuffers[stage].SetBindings(descriptors); _gpUniformBuffersDirty = true; } diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs b/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs new file mode 100644 index 0000000000..1734f08a22 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/CachedShaderBindings.cs @@ -0,0 +1,103 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Engine; +using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Shader; +using System; +using System.Linq; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + /// + /// A collection of shader bindings ready for insertion into the buffer and texture managers. + /// + internal class CachedShaderBindings + { + public TextureBindingInfo[][] TextureBindings { get; } + public TextureBindingInfo[][] ImageBindings { get; } + public BufferDescriptor[][] ConstantBufferBindings { get; } + public BufferDescriptor[][] StorageBufferBindings { get; } + + public int MaxTextureBinding { get; } + public int MaxImageBinding { get; } + + /// + /// Create a new cached shader bindings collection. + /// + /// Whether the shader is for compute + /// The stages used by the shader + public CachedShaderBindings(bool isCompute, CachedShaderStage[] stages) + { + int stageCount = isCompute ? 1 : Constants.ShaderStages; + + TextureBindings = new TextureBindingInfo[stageCount][]; + ImageBindings = new TextureBindingInfo[stageCount][]; + ConstantBufferBindings = new BufferDescriptor[stageCount][]; + StorageBufferBindings = new BufferDescriptor[stageCount][]; + + int maxTextureBinding = -1; + int maxImageBinding = -1; + int offset = isCompute ? 0 : 1; + + for (int i = 0; i < stageCount; i++) + { + CachedShaderStage stage = stages[i + offset]; + + if (stage == null) + { + TextureBindings[i] = Array.Empty(); + ImageBindings[i] = Array.Empty(); + ConstantBufferBindings[i] = Array.Empty(); + StorageBufferBindings[i] = Array.Empty(); + + continue; + } + + TextureBindings[i] = stage.Info.Textures.Select(descriptor => + { + Target target = ShaderTexture.GetTarget(descriptor.Type); + + var result = new TextureBindingInfo( + target, + descriptor.Binding, + descriptor.CbufSlot, + descriptor.HandleIndex, + descriptor.Flags); + + if (descriptor.Binding > maxTextureBinding) + { + maxTextureBinding = descriptor.Binding; + } + + return result; + }).ToArray(); + + ImageBindings[i] = stage.Info.Images.Select(descriptor => + { + Target target = ShaderTexture.GetTarget(descriptor.Type); + Format format = ShaderTexture.GetFormat(descriptor.Format); + + var result = new TextureBindingInfo( + target, + format, + descriptor.Binding, + descriptor.CbufSlot, + descriptor.HandleIndex, + descriptor.Flags); + + if (descriptor.Binding > maxImageBinding) + { + maxImageBinding = descriptor.Binding; + } + + return result; + }).ToArray(); + + ConstantBufferBindings[i] = stage.Info.CBuffers.ToArray(); + StorageBufferBindings[i] = stage.Info.SBuffers.ToArray(); + } + + MaxTextureBinding = maxTextureBinding; + MaxImageBinding = maxImageBinding; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs b/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs index 69fcb27804..ff9c39a197 100644 --- a/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs +++ b/Ryujinx.Graphics.Gpu/Shader/CachedShaderProgram.cs @@ -24,6 +24,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public CachedShaderStage[] Shaders { get; } + /// + /// Cached shader bindings, ready for placing into the bindings manager. + /// + public CachedShaderBindings Bindings { get; } + /// /// Creates a new instance of the shader bundle. /// @@ -37,6 +42,7 @@ namespace Ryujinx.Graphics.Gpu.Shader Shaders = shaders; SpecializationState.Prepare(shaders); + Bindings = new CachedShaderBindings(shaders.Length == 1, shaders); } ///