From b0af010247a2bc1d9af1fb1068d4fad0319ad216 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 19 Sep 2021 13:03:05 +0100 Subject: [PATCH] Set texture/image bindings in place rather than allocating and passing an array (#2647) * Remove allocations for texture bindings and state * Rent rather than stackalloc + copy A bit faster. --- .../Engine/Compute/ComputeClass.cs | 8 +- .../Engine/Threed/StateUpdater.cs | 12 +-- .../Image/TextureBindingsManager.cs | 86 +++++++++++++++---- Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 36 ++++---- 4 files changed, 95 insertions(+), 47 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index a0304308d8..8469f1aed0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -191,7 +191,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers); _channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers); - var textureBindings = new TextureBindingInfo[info.Textures.Count]; + TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count); for (int index = 0; index < info.Textures.Count; index++) { @@ -207,9 +207,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute descriptor.Flags); } - _channel.TextureManager.SetComputeTextures(textureBindings); - - var imageBindings = new TextureBindingInfo[info.Images.Count]; + TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count); for (int index = 0; index < info.Images.Count; index++) { @@ -227,8 +225,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute descriptor.Flags); } - _channel.TextureManager.SetComputeImages(imageBindings); - _channel.TextureManager.CommitComputeBindings(); _channel.BufferManager.CommitComputeBindings(); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index a6953cd279..f429ae9092 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -970,14 +970,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (info == null) { - _channel.TextureManager.SetGraphicsTextures(stage, Array.Empty()); - _channel.TextureManager.SetGraphicsImages(stage, Array.Empty()); + _channel.TextureManager.RentGraphicsTextureBindings(stage, 0); + _channel.TextureManager.RentGraphicsImageBindings(stage, 0); _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null); _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null); continue; } - var textureBindings = new TextureBindingInfo[info.Textures.Count]; + Span textureBindings = _channel.TextureManager.RentGraphicsTextureBindings(stage, info.Textures.Count); for (int index = 0; index < info.Textures.Count; index++) { @@ -993,9 +993,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed descriptor.Flags); } - _channel.TextureManager.SetGraphicsTextures(stage, textureBindings); - - var imageBindings = new TextureBindingInfo[info.Images.Count]; + TextureBindingInfo[] imageBindings = _channel.TextureManager.RentGraphicsImageBindings(stage, info.Images.Count); for (int index = 0; index < info.Images.Count; index++) { @@ -1013,8 +1011,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed descriptor.Flags); } - _channel.TextureManager.SetGraphicsImages(stage, imageBindings); - _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 6c48fc0eaf..597c527dbc 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// class TextureBindingsManager : IDisposable { + private const int InitialTextureStateSize = 32; + private const int InitialImageStateSize = 8; + private const int HandleHigh = 16; private const int HandleMask = (1 << HandleHigh) - 1; @@ -43,6 +46,9 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly TextureStatePerStage[][] _textureState; private readonly TextureStatePerStage[][] _imageState; + private int[] _textureBindingsCount; + private int[] _imageBindingsCount; + private int _textureBufferIndex; private bool _rebind; @@ -72,6 +78,18 @@ namespace Ryujinx.Graphics.Gpu.Image _textureState = new TextureStatePerStage[stages][]; _imageState = new TextureStatePerStage[stages][]; + _textureBindingsCount = new int[stages]; + _imageBindingsCount = new int[stages]; + + for (int stage = 0; stage < stages; stage++) + { + _textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize]; + _imageBindings[stage] = new TextureBindingInfo[InitialImageStateSize]; + + _textureState[stage] = new TextureStatePerStage[InitialTextureStateSize]; + _imageState[stage] = new TextureStatePerStage[InitialImageStateSize]; + } + _scales = new float[64]; for (int i = 0; i < 64; i++) @@ -81,25 +99,57 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Binds textures for a given shader stage. + /// Rents the texture bindings array for a given stage, so that they can be modified. /// /// Shader stage number, or 0 for compute shaders - /// Texture bindings - public void SetTextures(int stage, TextureBindingInfo[] bindings) + /// The number of bindings needed + /// The texture bindings array + public TextureBindingInfo[] RentTextureBindings(int stage, int count) { - _textureBindings[stage] = bindings; - _textureState[stage] = new TextureStatePerStage[bindings.Length]; + 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; + + return _textureBindings[stage]; } /// - /// Binds images for a given shader stage. + /// Rents the image bindings array for a given stage, so that they can be modified. /// /// Shader stage number, or 0 for compute shaders - /// Image bindings - public void SetImages(int stage, TextureBindingInfo[] bindings) + /// The number of bindings needed + /// The image bindings array + public TextureBindingInfo[] RentImageBindings(int stage, int count) { - _imageBindings[stage] = bindings; - _imageState[stage] = new TextureStatePerStage[bindings.Length]; + 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]; + + for (int i = 0; i < toClear; i++) + { + state[i] = new TextureStatePerStage(); + } + + _imageBindingsCount[stage] = count; + + return _imageBindings[stage]; } /// @@ -235,7 +285,7 @@ namespace Ryujinx.Graphics.Gpu.Image { if (_scaleChanged) { - _context.Renderer.Pipeline.UpdateRenderScale(stage, _scales, _textureBindings[stageIndex]?.Length ?? 0, _imageBindings[stageIndex]?.Length ?? 0); + _context.Renderer.Pipeline.UpdateRenderScale(stage, _scales, _textureBindingsCount[stageIndex], _imageBindingsCount[stageIndex]); _scaleChanged = false; } @@ -285,7 +335,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// The stage number of the specified shader stage private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex) { - if (_textureBindings[stageIndex] == null || _textureBindings[stageIndex].Length == 0) + int textureCount = _textureBindingsCount[stageIndex]; + if (textureCount == 0) { return; } @@ -304,7 +355,7 @@ namespace Ryujinx.Graphics.Gpu.Image return; } - for (int index = 0; index < _textureBindings[stageIndex].Length; index++) + for (int index = 0; index < textureCount; index++) { TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index]; @@ -381,21 +432,22 @@ namespace Ryujinx.Graphics.Gpu.Image /// The stage number of the specified shader stage private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex) { - if (_imageBindings[stageIndex] == null) + int imageCount = _imageBindingsCount[stageIndex]; + if (imageCount == 0) { return; } - if (pool == null && _imageBindings[stageIndex].Length != 0) + if (pool == null) { Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set."); return; } // Scales for images appear after the texture ones. - int baseScaleIndex = _textureBindings[stageIndex]?.Length ?? 0; + int baseScaleIndex = _textureBindingsCount[stageIndex]; - for (int index = 0; index < _imageBindings[stageIndex].Length; index++) + for (int index = 0; index < imageCount; index++) { TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index]; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 4db6532b81..1d7b8df2f7 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -43,41 +43,45 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Sets texture bindings on the compute pipeline. + /// Rents the texture bindings array of the compute pipeline. /// - /// The texture bindings - public void SetComputeTextures(TextureBindingInfo[] bindings) + /// The number of bindings needed + /// The texture bindings array + public TextureBindingInfo[] RentComputeTextureBindings(int count) { - _cpBindingsManager.SetTextures(0, bindings); + return _cpBindingsManager.RentTextureBindings(0, count); } /// - /// Sets texture bindings on the graphics pipeline. + /// Rents the texture bindings array for a given stage on the graphics pipeline. /// /// The index of the shader stage to bind the textures - /// The texture bindings - public void SetGraphicsTextures(int stage, TextureBindingInfo[] bindings) + /// The number of bindings needed + /// The texture bindings array + public TextureBindingInfo[] RentGraphicsTextureBindings(int stage, int count) { - _gpBindingsManager.SetTextures(stage, bindings); + return _gpBindingsManager.RentTextureBindings(stage, count); } /// - /// Sets image bindings on the compute pipeline. + /// Rents the image bindings array of the compute pipeline. /// - /// The image bindings - public void SetComputeImages(TextureBindingInfo[] bindings) + /// The number of bindings needed + /// The image bindings array + public TextureBindingInfo[] RentComputeImageBindings(int count) { - _cpBindingsManager.SetImages(0, bindings); + return _cpBindingsManager.RentImageBindings(0, count); } /// - /// Sets image bindings on the graphics pipeline. + /// Rents the image bindings array for a given stage on the graphics pipeline. /// /// The index of the shader stage to bind the images - /// The image bindings - public void SetGraphicsImages(int stage, TextureBindingInfo[] bindings) + /// The number of bindings needed + /// The image bindings array + public TextureBindingInfo[] RentGraphicsImageBindings(int stage, int count) { - _gpBindingsManager.SetImages(stage, bindings); + return _gpBindingsManager.RentImageBindings(stage, count); } ///