From 795539bc821dd51ff7fb2b1f2c851881b168b03d Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 25 Jan 2024 18:29:53 +0000 Subject: [PATCH] Vulkan: Use staging buffer for temporary constants (#6168) * Vulkan: Use staging buffer for temporary constants Helper shaders and post processing effects typically need some parameters to tell them what to do, which we pass via constant buffers that are created and destroyed each time. This can vary in cost between different Vulkan drivers. It shows up on profiles on mesa and MoltenVK, so it's worth avoiding. Some games only do it once (BlitColor for present), others multiple times. It's also done for post processing filters and FSR upscaling, which creates two buffers. For mirrors, I added the ability to reserve a range on the staging buffer for use as any type of binding. This PR allows these constant buffers to be instead temporarily allocated on the staging buffer, skipping allocation and buffer management costs entirely. Two temporary allocations do remain: - DrawTexture, because it doesn't have access to the command buffer scope - Index buffer indirect conversion, because one of them is a storage buffer and thus is a little more complicated. There's a small cost in that the uniform buffer takes up more space due to alignment requirements. At worst that's 256 bytes (on a GTX 1070) but more modern GPUs should have a better time. Worth testing across different games and post effects to make sure they still work. * Use temporary buffer for ConvertIndexBufferIndirect * Simplify alignment passing for now * Fix shader params length for CopyIncompatibleFormats * Set data for helpershaders without overlap checks The data is in the staging buffer, so its usage range is guarded using that. --- src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 8 +- src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 50 +++++++++- .../Effects/FsrScalingFilter.cs | 19 ++-- .../Effects/FxaaPostProcessingEffect.cs | 8 +- .../Effects/SmaaPostProcessingEffect.cs | 9 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 92 ++++++++----------- src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs | 23 ++++- 7 files changed, 126 insertions(+), 83 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index bdd5d3856..3673ee5a1 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -4,6 +4,7 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; @@ -384,7 +385,7 @@ namespace Ryujinx.Graphics.Vulkan var baseData = new Span((void*)(_map + offset), size); var modData = _pendingData.AsSpan(offset, size); - StagingBufferReserved? newMirror = _gd.BufferManager.StagingBuffer.TryReserveData(cbs, size, (int)_gd.Capabilities.MinResourceAlignment); + StagingBufferReserved? newMirror = _gd.BufferManager.StagingBuffer.TryReserveData(cbs, size); if (newMirror != null) { @@ -838,6 +839,11 @@ namespace Ryujinx.Graphics.Vulkan } } + public unsafe void SetDataUnchecked(int offset, ReadOnlySpan data) where T : unmanaged + { + SetDataUnchecked(offset, MemoryMarshal.AsBytes(data)); + } + public void SetDataInline(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan data) { if (!TryPushData(cbs, endRenderPass, dstOffset, data)) diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index e9ac98847..33289a0e0 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -9,6 +9,36 @@ using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan { + readonly struct ScopedTemporaryBuffer : IDisposable + { + private readonly BufferManager _bufferManager; + private readonly bool _isReserved; + + public readonly BufferRange Range; + public readonly BufferHolder Holder; + + public BufferHandle Handle => Range.Handle; + public int Offset => Range.Offset; + + public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved) + { + _bufferManager = bufferManager; + + Range = new BufferRange(handle, offset, size); + Holder = holder; + + _isReserved = isReserved; + } + + public void Dispose() + { + if (!_isReserved) + { + _bufferManager.Delete(Range.Handle); + } + } + } + class BufferManager : IDisposable { public const MemoryPropertyFlags DefaultBufferMemoryFlags = @@ -238,6 +268,23 @@ namespace Ryujinx.Graphics.Vulkan return Unsafe.As(ref handle64); } + public ScopedTemporaryBuffer ReserveOrCreate(VulkanRenderer gd, CommandBufferScoped cbs, int size) + { + StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size); + + if (result.HasValue) + { + return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true); + } + else + { + // Create a temporary buffer. + BufferHandle handle = CreateWithHandle(gd, size, out BufferHolder holder); + + return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false); + } + } + public unsafe MemoryRequirements GetHostImportedUsageRequirements(VulkanRenderer gd) { var usage = HostImportedBufferUsageFlags; @@ -635,13 +682,14 @@ namespace Ryujinx.Graphics.Vulkan { if (disposing) { + StagingBuffer.Dispose(); + foreach (BufferHolder buffer in _buffers) { buffer.Dispose(); } _buffers.Clear(); - StagingBuffer.Dispose(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index 23acdcf8f..5c0fc468b 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -142,19 +142,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects }; int rangeSize = dimensionsBuffer.Length * sizeof(float); - var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); - _renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer); + using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, dimensionsBuffer); - ReadOnlySpan sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f) }; - var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float)); - _renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer); + ReadOnlySpan sharpeningBufferData = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f) }; + using var sharpeningBuffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, sizeof(float)); + sharpeningBuffer.Holder.SetDataUnchecked(sharpeningBuffer.Offset, sharpeningBufferData); int threadGroupWorkRegionDim = 16; int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); _pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -162,16 +161,12 @@ namespace Ryujinx.Graphics.Vulkan.Effects // Sharpening pass _pipeline.SetProgram(_sharpeningProgram); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler); - var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float)); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningBuffer.Range) }); _pipeline.SetImage(0, destinationTexture); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); _pipeline.Finish(); - - _renderer.BufferManager.Delete(bufferHandle); - _renderer.BufferManager.Delete(sharpeningBufferHandle); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index 67e461e51..a7dd8eee8 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -66,12 +66,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects ReadOnlySpan resolutionBuffer = stackalloc float[] { view.Width, view.Height }; int rangeSize = resolutionBuffer.Length * sizeof(float); - var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); + using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize); - _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); + buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer); - var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); @@ -79,7 +78,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); - _renderer.BufferManager.Delete(bufferHandle); _pipeline.ComputeBarrier(); _pipeline.Finish(); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index c521f2273..802b73b86 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -215,11 +215,10 @@ namespace Ryujinx.Graphics.Vulkan.Effects ReadOnlySpan resolutionBuffer = stackalloc float[] { view.Width, view.Height }; int rangeSize = resolutionBuffer.Length * sizeof(float); - var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); + using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize); - _renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer); - var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); _pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.ComputeBarrier(); @@ -245,8 +244,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects _pipeline.Finish(); - _renderer.BufferManager.Delete(bufferHandle); - return _outputTexture; } diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index deaf81625..ce84f7521 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -430,11 +430,11 @@ namespace Ryujinx.Graphics.Vulkan (region[2], region[3]) = (region[3], region[2]); } - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, RegionBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, region); + buffer.Holder.SetDataUnchecked(buffer.Offset, region); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) }); Span viewports = stackalloc Viewport[1]; @@ -490,8 +490,6 @@ namespace Ryujinx.Graphics.Vulkan } _pipeline.Finish(gd, cbs); - - gd.BufferManager.Delete(bufferHandle); } private void BlitDepthStencil( @@ -527,11 +525,11 @@ namespace Ryujinx.Graphics.Vulkan (region[2], region[3]) = (region[3], region[2]); } - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, RegionBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, region); + buffer.Holder.SetDataUnchecked(buffer.Offset, region); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) }); Span viewports = stackalloc Viewport[1]; @@ -582,8 +580,6 @@ namespace Ryujinx.Graphics.Vulkan } _pipeline.Finish(gd, cbs); - - gd.BufferManager.Delete(bufferHandle); } private static TextureView CreateDepthOrStencilView(TextureView depthStencilTexture, DepthStencilMode depthStencilMode) @@ -681,11 +677,11 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ClearColorBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, clearColor); + buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) }); Span viewports = stackalloc Viewport[1]; @@ -721,8 +717,6 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); - - gd.BufferManager.Delete(bufferHandle); } public void Clear( @@ -745,11 +739,11 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetCommandBuffer(cbs); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ClearColorBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, stackalloc float[] { depthValue }); + buffer.Holder.SetDataUnchecked(buffer.Offset, stackalloc float[] { depthValue }); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) }); Span viewports = stackalloc Viewport[1]; @@ -771,8 +765,6 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask)); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); - - gd.BufferManager.Delete(bufferHandle); } public void DrawTexture( @@ -878,13 +870,13 @@ namespace Ryujinx.Graphics.Vulkan shaderParams[2] = size; shaderParams[3] = srcOffset; - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, shaderParams); + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); _pipeline.SetCommandBuffer(cbs); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) }); Span> sbRanges = new Auto[2]; @@ -896,8 +888,6 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetProgram(_programStrideChange); _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); - gd.BufferManager.Delete(bufferHandle); - _pipeline.Finish(gd, cbs); } else @@ -1025,7 +1015,7 @@ namespace Ryujinx.Graphics.Vulkan { const int ParamsBufferSize = 4; - Span shaderParams = stackalloc int[sizeof(int)]; + Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; int srcBpp = src.Info.BytesPerPixel; int dstBpp = dst.Info.BytesPerPixel; @@ -1034,9 +1024,9 @@ namespace Ryujinx.Graphics.Vulkan shaderParams[0] = BitOperations.Log2((uint)ratio); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, shaderParams); + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); TextureView.InsertImageBarrier( gd.Api, @@ -1064,7 +1054,7 @@ namespace Ryujinx.Graphics.Vulkan var srcFormat = GetFormat(componentSize, srcBpp / componentSize); var dstFormat = GetFormat(componentSize, dstBpp / componentSize); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) }); for (int l = 0; l < levels; l++) { @@ -1093,8 +1083,6 @@ namespace Ryujinx.Graphics.Vulkan } } - gd.BufferManager.Delete(bufferHandle); - _pipeline.Finish(gd, cbs); TextureView.InsertImageBarrier( @@ -1128,9 +1116,9 @@ namespace Ryujinx.Graphics.Vulkan (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples)); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, shaderParams); + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); TextureView.InsertImageBarrier( gd.Api, @@ -1147,7 +1135,7 @@ namespace Ryujinx.Graphics.Vulkan 1); _pipeline.SetCommandBuffer(cbs); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) }); if (isDepthOrStencil) { @@ -1226,8 +1214,6 @@ namespace Ryujinx.Graphics.Vulkan } } - gd.BufferManager.Delete(bufferHandle); - _pipeline.Finish(gd, cbs); TextureView.InsertImageBarrier( @@ -1261,9 +1247,9 @@ namespace Ryujinx.Graphics.Vulkan (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples)); - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, shaderParams); + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); TextureView.InsertImageBarrier( gd.Api, @@ -1299,7 +1285,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) }); if (isDepthOrStencil) { @@ -1364,8 +1350,6 @@ namespace Ryujinx.Graphics.Vulkan } } - gd.BufferManager.Delete(bufferHandle); - _pipeline.Finish(gd, cbs); TextureView.InsertImageBarrier( @@ -1616,10 +1600,11 @@ namespace Ryujinx.Graphics.Vulkan pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]); - var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer); + using var patternScoped = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize); + var patternBuffer = patternScoped.Holder; var patternBufferAuto = patternBuffer.GetBuffer(); - gd.BufferManager.SetData(patternBufferHandle, 0, shaderParams); + patternBuffer.SetDataUnchecked(patternScoped.Offset, shaderParams); _pipeline.SetCommandBuffer(cbs); @@ -1635,7 +1620,8 @@ namespace Ryujinx.Graphics.Vulkan indirectDataSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) }); - _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() }); + _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer() }); + _pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(3, patternScoped.Range) }); _pipeline.SetProgram(_programConvertIndirectData); _pipeline.DispatchCompute(1, 1, 1); @@ -1643,12 +1629,12 @@ namespace Ryujinx.Graphics.Vulkan BufferHolder.InsertBufferBarrier( gd, cbs.CommandBuffer, - patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value, + patternBufferAuto.Get(cbs, patternScoped.Offset + ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value, AccessFlags.ShaderWriteBit, AccessFlags.IndirectCommandReadBit, PipelineStageFlags.ComputeShaderBit, PipelineStageFlags.DrawIndirectBit, - ParamsIndirectDispatchOffset, + patternScoped.Offset + ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize); BufferHolder.InsertBufferBarrier( @@ -1662,11 +1648,11 @@ namespace Ryujinx.Graphics.Vulkan 0, convertedCount * outputIndexSize); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternBufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternScoped.Handle, patternScoped.Offset, ParamsBufferSize)) }); _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() }); _pipeline.SetProgram(_programConvertIndexBuffer); - _pipeline.DispatchComputeIndirect(patternBufferAuto, ParamsIndirectDispatchOffset); + _pipeline.DispatchComputeIndirect(patternBufferAuto, patternScoped.Offset + ParamsIndirectDispatchOffset); BufferHolder.InsertBufferBarrier( gd, @@ -1679,8 +1665,6 @@ namespace Ryujinx.Graphics.Vulkan 0, convertedCount * outputIndexSize); - gd.BufferManager.Delete(patternBufferHandle); - _pipeline.Finish(gd, cbs); } @@ -1726,13 +1710,13 @@ namespace Ryujinx.Graphics.Vulkan shaderParams[0] = pixelCount; shaderParams[1] = dstOffset; - var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize); - gd.BufferManager.SetData(bufferHandle, 0, shaderParams); + buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); _pipeline.SetCommandBuffer(cbs); - _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) }); Span> sbRanges = new Auto[2]; @@ -1744,8 +1728,6 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetProgram(_programConvertD32S8ToD24S8); _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1); - gd.BufferManager.Delete(bufferHandle); - _pipeline.Finish(gd, cbs); BufferHolder.InsertBufferBarrier( diff --git a/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs index 3a02a28dc..90a47bb67 100644 --- a/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/StagingBuffer.cs @@ -1,5 +1,6 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; using System; using System.Collections.Generic; using System.Diagnostics; @@ -29,6 +30,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly VulkanRenderer _gd; private readonly BufferHolder _buffer; + private readonly int _resourceAlignment; + + public readonly BufferHandle Handle; private readonly struct PendingCopy { @@ -48,9 +52,10 @@ namespace Ryujinx.Graphics.Vulkan public StagingBuffer(VulkanRenderer gd, BufferManager bufferManager) { _gd = gd; - _buffer = bufferManager.Create(gd, BufferSize); + Handle = bufferManager.CreateWithHandle(gd, BufferSize, out _buffer); _pendingCopies = new Queue(); _freeSize = BufferSize; + _resourceAlignment = (int)gd.Capabilities.MinResourceAlignment; } public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan data) @@ -197,7 +202,7 @@ namespace Ryujinx.Graphics.Vulkan /// Reserve a range on the staging buffer for the current command buffer and upload data to it. /// /// Command buffer to reserve the data on - /// The data to upload + /// The minimum size the reserved data requires /// The required alignment for the buffer offset /// The reserved range of the staging buffer public unsafe StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size, int alignment) @@ -223,6 +228,18 @@ namespace Ryujinx.Graphics.Vulkan return ReserveDataImpl(cbs, size, alignment); } + /// + /// Reserve a range on the staging buffer for the current command buffer and upload data to it. + /// Uses the most permissive byte alignment. + /// + /// Command buffer to reserve the data on + /// The minimum size the reserved data requires + /// The reserved range of the staging buffer + public unsafe StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size) + { + return TryReserveData(cbs, size, _resourceAlignment); + } + private bool WaitFreeCompleted(CommandBufferPool cbp) { if (_pendingCopies.TryPeek(out var pc)) @@ -263,7 +280,7 @@ namespace Ryujinx.Graphics.Vulkan { if (disposing) { - _buffer.Dispose(); + _gd.BufferManager.Delete(Handle); while (_pendingCopies.TryDequeue(out var pc)) {