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.
This commit is contained in:
riperiperi 2024-01-25 18:29:53 +00:00 committed by GitHub
parent dd2e851e95
commit 795539bc82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 126 additions and 83 deletions

View File

@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using VkBuffer = Silk.NET.Vulkan.Buffer; using VkBuffer = Silk.NET.Vulkan.Buffer;
using VkFormat = Silk.NET.Vulkan.Format; using VkFormat = Silk.NET.Vulkan.Format;
@ -384,7 +385,7 @@ namespace Ryujinx.Graphics.Vulkan
var baseData = new Span<byte>((void*)(_map + offset), size); var baseData = new Span<byte>((void*)(_map + offset), size);
var modData = _pendingData.AsSpan(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) if (newMirror != null)
{ {
@ -838,6 +839,11 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public unsafe void SetDataUnchecked<T>(int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetDataUnchecked(offset, MemoryMarshal.AsBytes(data));
}
public void SetDataInline(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan<byte> data) public void SetDataInline(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan<byte> data)
{ {
if (!TryPushData(cbs, endRenderPass, dstOffset, data)) if (!TryPushData(cbs, endRenderPass, dstOffset, data))

View File

@ -9,6 +9,36 @@ using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan 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 class BufferManager : IDisposable
{ {
public const MemoryPropertyFlags DefaultBufferMemoryFlags = public const MemoryPropertyFlags DefaultBufferMemoryFlags =
@ -238,6 +268,23 @@ namespace Ryujinx.Graphics.Vulkan
return Unsafe.As<ulong, BufferHandle>(ref handle64); return Unsafe.As<ulong, BufferHandle>(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) public unsafe MemoryRequirements GetHostImportedUsageRequirements(VulkanRenderer gd)
{ {
var usage = HostImportedBufferUsageFlags; var usage = HostImportedBufferUsageFlags;
@ -635,13 +682,14 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (disposing) if (disposing)
{ {
StagingBuffer.Dispose();
foreach (BufferHolder buffer in _buffers) foreach (BufferHolder buffer in _buffers)
{ {
buffer.Dispose(); buffer.Dispose();
} }
_buffers.Clear(); _buffers.Clear();
StagingBuffer.Dispose();
} }
} }

View File

@ -142,19 +142,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects
}; };
int rangeSize = dimensionsBuffer.Length * sizeof(float); int rangeSize = dimensionsBuffer.Length * sizeof(float);
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize); using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize);
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer); buffer.Holder.SetDataUnchecked(buffer.Offset, dimensionsBuffer);
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f) }; ReadOnlySpan<float> sharpeningBufferData = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f) };
var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float)); using var sharpeningBuffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, sizeof(float));
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer); sharpeningBuffer.Holder.SetDataUnchecked(sharpeningBuffer.Offset, sharpeningBufferData);
int threadGroupWorkRegionDim = 16; int threadGroupWorkRegionDim = 16;
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
_pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
@ -162,16 +161,12 @@ namespace Ryujinx.Graphics.Vulkan.Effects
// Sharpening pass // Sharpening pass
_pipeline.SetProgram(_sharpeningProgram); _pipeline.SetProgram(_sharpeningProgram);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float)); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningBuffer.Range) });
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
_pipeline.SetImage(0, destinationTexture); _pipeline.SetImage(0, destinationTexture);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
_pipeline.Finish(); _pipeline.Finish();
_renderer.BufferManager.Delete(bufferHandle);
_renderer.BufferManager.Delete(sharpeningBufferHandle);
} }
} }
} }

View File

@ -66,12 +66,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
int rangeSize = resolutionBuffer.Length * sizeof(float); 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, buffer.Range) });
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
var dispatchY = BitUtils.DivRoundUp(view.Height, 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.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_renderer.BufferManager.Delete(bufferHandle);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
_pipeline.Finish(); _pipeline.Finish();

View File

@ -215,11 +215,10 @@ namespace Ryujinx.Graphics.Vulkan.Effects
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
int rangeSize = resolutionBuffer.Length * sizeof(float); 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, buffer.Range) });
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
_pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
@ -245,8 +244,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.Finish(); _pipeline.Finish();
_renderer.BufferManager.Delete(bufferHandle);
return _outputTexture; return _outputTexture;
} }

View File

@ -430,11 +430,11 @@ namespace Ryujinx.Graphics.Vulkan
(region[2], region[3]) = (region[3], region[2]); (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<float>(bufferHandle, 0, region); buffer.Holder.SetDataUnchecked<float>(buffer.Offset, region);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) });
Span<Viewport> viewports = stackalloc Viewport[1]; Span<Viewport> viewports = stackalloc Viewport[1];
@ -490,8 +490,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
gd.BufferManager.Delete(bufferHandle);
} }
private void BlitDepthStencil( private void BlitDepthStencil(
@ -527,11 +525,11 @@ namespace Ryujinx.Graphics.Vulkan
(region[2], region[3]) = (region[3], region[2]); (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<float>(bufferHandle, 0, region); buffer.Holder.SetDataUnchecked<float>(buffer.Offset, region);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) });
Span<Viewport> viewports = stackalloc Viewport[1]; Span<Viewport> viewports = stackalloc Viewport[1];
@ -582,8 +580,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
gd.BufferManager.Delete(bufferHandle);
} }
private static TextureView CreateDepthOrStencilView(TextureView depthStencilTexture, DepthStencilMode depthStencilMode) private static TextureView CreateDepthOrStencilView(TextureView depthStencilTexture, DepthStencilMode depthStencilMode)
@ -681,11 +677,11 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetCommandBuffer(cbs); _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<Viewport> viewports = stackalloc Viewport[1]; Span<Viewport> viewports = stackalloc Viewport[1];
@ -721,8 +717,6 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish(); _pipeline.Finish();
gd.BufferManager.Delete(bufferHandle);
} }
public void Clear( public void Clear(
@ -745,11 +739,11 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetCommandBuffer(cbs); _pipeline.SetCommandBuffer(cbs);
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ClearColorBufferSize);
gd.BufferManager.SetData<float>(bufferHandle, 0, stackalloc float[] { depthValue }); buffer.Holder.SetDataUnchecked<float>(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<Viewport> viewports = stackalloc Viewport[1]; Span<Viewport> viewports = stackalloc Viewport[1];
@ -771,8 +765,6 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask));
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish(); _pipeline.Finish();
gd.BufferManager.Delete(bufferHandle);
} }
public void DrawTexture( public void DrawTexture(
@ -878,13 +870,13 @@ namespace Ryujinx.Graphics.Vulkan
shaderParams[2] = size; shaderParams[2] = size;
shaderParams[3] = srcOffset; shaderParams[3] = srcOffset;
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
_pipeline.SetCommandBuffer(cbs); _pipeline.SetCommandBuffer(cbs);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2]; Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
@ -896,8 +888,6 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetProgram(_programStrideChange); _pipeline.SetProgram(_programStrideChange);
_pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1);
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
} }
else else
@ -1025,7 +1015,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
const int ParamsBufferSize = 4; const int ParamsBufferSize = 4;
Span<int> shaderParams = stackalloc int[sizeof(int)]; Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
int srcBpp = src.Info.BytesPerPixel; int srcBpp = src.Info.BytesPerPixel;
int dstBpp = dst.Info.BytesPerPixel; int dstBpp = dst.Info.BytesPerPixel;
@ -1034,9 +1024,9 @@ namespace Ryujinx.Graphics.Vulkan
shaderParams[0] = BitOperations.Log2((uint)ratio); 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<int>(bufferHandle, 0, shaderParams); buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
TextureView.InsertImageBarrier( TextureView.InsertImageBarrier(
gd.Api, gd.Api,
@ -1064,7 +1054,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcFormat = GetFormat(componentSize, srcBpp / componentSize); var srcFormat = GetFormat(componentSize, srcBpp / componentSize);
var dstFormat = GetFormat(componentSize, dstBpp / 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++) for (int l = 0; l < levels; l++)
{ {
@ -1093,8 +1083,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
TextureView.InsertImageBarrier( TextureView.InsertImageBarrier(
@ -1128,9 +1116,9 @@ namespace Ryujinx.Graphics.Vulkan
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)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<int>(bufferHandle, 0, shaderParams); buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
TextureView.InsertImageBarrier( TextureView.InsertImageBarrier(
gd.Api, gd.Api,
@ -1147,7 +1135,7 @@ namespace Ryujinx.Graphics.Vulkan
1); 1);
_pipeline.SetCommandBuffer(cbs); _pipeline.SetCommandBuffer(cbs);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
if (isDepthOrStencil) if (isDepthOrStencil)
{ {
@ -1226,8 +1214,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
TextureView.InsertImageBarrier( TextureView.InsertImageBarrier(
@ -1261,9 +1247,9 @@ namespace Ryujinx.Graphics.Vulkan
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples); (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)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<int>(bufferHandle, 0, shaderParams); buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
TextureView.InsertImageBarrier( TextureView.InsertImageBarrier(
gd.Api, gd.Api,
@ -1299,7 +1285,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetViewports(viewports); _pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _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) if (isDepthOrStencil)
{ {
@ -1364,8 +1350,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
TextureView.InsertImageBarrier( TextureView.InsertImageBarrier(
@ -1616,10 +1600,11 @@ namespace Ryujinx.Graphics.Vulkan
pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]); 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(); var patternBufferAuto = patternBuffer.GetBuffer();
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams); patternBuffer.SetDataUnchecked<int>(patternScoped.Offset, shaderParams);
_pipeline.SetCommandBuffer(cbs); _pipeline.SetCommandBuffer(cbs);
@ -1635,7 +1620,8 @@ namespace Ryujinx.Graphics.Vulkan
indirectDataSize); indirectDataSize);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) }); _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.SetProgram(_programConvertIndirectData);
_pipeline.DispatchCompute(1, 1, 1); _pipeline.DispatchCompute(1, 1, 1);
@ -1643,12 +1629,12 @@ namespace Ryujinx.Graphics.Vulkan
BufferHolder.InsertBufferBarrier( BufferHolder.InsertBufferBarrier(
gd, gd,
cbs.CommandBuffer, cbs.CommandBuffer,
patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value, patternBufferAuto.Get(cbs, patternScoped.Offset + ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value,
AccessFlags.ShaderWriteBit, AccessFlags.ShaderWriteBit,
AccessFlags.IndirectCommandReadBit, AccessFlags.IndirectCommandReadBit,
PipelineStageFlags.ComputeShaderBit, PipelineStageFlags.ComputeShaderBit,
PipelineStageFlags.DrawIndirectBit, PipelineStageFlags.DrawIndirectBit,
ParamsIndirectDispatchOffset, patternScoped.Offset + ParamsIndirectDispatchOffset,
ParamsIndirectDispatchSize); ParamsIndirectDispatchSize);
BufferHolder.InsertBufferBarrier( BufferHolder.InsertBufferBarrier(
@ -1662,11 +1648,11 @@ namespace Ryujinx.Graphics.Vulkan
0, 0,
convertedCount * outputIndexSize); 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.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
_pipeline.SetProgram(_programConvertIndexBuffer); _pipeline.SetProgram(_programConvertIndexBuffer);
_pipeline.DispatchComputeIndirect(patternBufferAuto, ParamsIndirectDispatchOffset); _pipeline.DispatchComputeIndirect(patternBufferAuto, patternScoped.Offset + ParamsIndirectDispatchOffset);
BufferHolder.InsertBufferBarrier( BufferHolder.InsertBufferBarrier(
gd, gd,
@ -1679,8 +1665,6 @@ namespace Ryujinx.Graphics.Vulkan
0, 0,
convertedCount * outputIndexSize); convertedCount * outputIndexSize);
gd.BufferManager.Delete(patternBufferHandle);
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
} }
@ -1726,13 +1710,13 @@ namespace Ryujinx.Graphics.Vulkan
shaderParams[0] = pixelCount; shaderParams[0] = pixelCount;
shaderParams[1] = dstOffset; shaderParams[1] = dstOffset;
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
_pipeline.SetCommandBuffer(cbs); _pipeline.SetCommandBuffer(cbs);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2]; Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
@ -1744,8 +1728,6 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetProgram(_programConvertD32S8ToD24S8); _pipeline.SetProgram(_programConvertD32S8ToD24S8);
_pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1); _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1);
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs); _pipeline.Finish(gd, cbs);
BufferHolder.InsertBufferBarrier( BufferHolder.InsertBufferBarrier(

View File

@ -1,5 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -29,6 +30,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly VulkanRenderer _gd; private readonly VulkanRenderer _gd;
private readonly BufferHolder _buffer; private readonly BufferHolder _buffer;
private readonly int _resourceAlignment;
public readonly BufferHandle Handle;
private readonly struct PendingCopy private readonly struct PendingCopy
{ {
@ -48,9 +52,10 @@ namespace Ryujinx.Graphics.Vulkan
public StagingBuffer(VulkanRenderer gd, BufferManager bufferManager) public StagingBuffer(VulkanRenderer gd, BufferManager bufferManager)
{ {
_gd = gd; _gd = gd;
_buffer = bufferManager.Create(gd, BufferSize); Handle = bufferManager.CreateWithHandle(gd, BufferSize, out _buffer);
_pendingCopies = new Queue<PendingCopy>(); _pendingCopies = new Queue<PendingCopy>();
_freeSize = BufferSize; _freeSize = BufferSize;
_resourceAlignment = (int)gd.Capabilities.MinResourceAlignment;
} }
public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data) public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> 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. /// Reserve a range on the staging buffer for the current command buffer and upload data to it.
/// </summary> /// </summary>
/// <param name="cbs">Command buffer to reserve the data on</param> /// <param name="cbs">Command buffer to reserve the data on</param>
/// <param name="data">The data to upload</param> /// <param name="size">The minimum size the reserved data requires</param>
/// <param name="alignment">The required alignment for the buffer offset</param> /// <param name="alignment">The required alignment for the buffer offset</param>
/// <returns>The reserved range of the staging buffer</returns> /// <returns>The reserved range of the staging buffer</returns>
public unsafe StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size, int alignment) public unsafe StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size, int alignment)
@ -223,6 +228,18 @@ namespace Ryujinx.Graphics.Vulkan
return ReserveDataImpl(cbs, size, alignment); return ReserveDataImpl(cbs, size, alignment);
} }
/// <summary>
/// Reserve a range on the staging buffer for the current command buffer and upload data to it.
/// Uses the most permissive byte alignment.
/// </summary>
/// <param name="cbs">Command buffer to reserve the data on</param>
/// <param name="size">The minimum size the reserved data requires</param>
/// <returns>The reserved range of the staging buffer</returns>
public unsafe StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size)
{
return TryReserveData(cbs, size, _resourceAlignment);
}
private bool WaitFreeCompleted(CommandBufferPool cbp) private bool WaitFreeCompleted(CommandBufferPool cbp)
{ {
if (_pendingCopies.TryPeek(out var pc)) if (_pendingCopies.TryPeek(out var pc))
@ -263,7 +280,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (disposing) if (disposing)
{ {
_buffer.Dispose(); _gd.BufferManager.Delete(Handle);
while (_pendingCopies.TryDequeue(out var pc)) while (_pendingCopies.TryDequeue(out var pc))
{ {