801b71a128
* dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0059 warnings * Address dotnet format CA1816 warnings * Fix new dotnet-format issues after rebase * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Format if-blocks correctly * Another rebase, another dotnet format run * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Run dotnet format style after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Run dotnet format after rebase * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Fix naming rule violations * Remove redundant code * Rename generics * Address review feedback * Remove SetOrigin
194 lines
5.7 KiB
C#
194 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
|
|
namespace Ryujinx.Graphics.Vulkan
|
|
{
|
|
class StagingBuffer : IDisposable
|
|
{
|
|
private const int BufferSize = 16 * 1024 * 1024;
|
|
|
|
private int _freeOffset;
|
|
private int _freeSize;
|
|
|
|
private readonly VulkanRenderer _gd;
|
|
private readonly BufferHolder _buffer;
|
|
|
|
private readonly struct PendingCopy
|
|
{
|
|
public FenceHolder Fence { get; }
|
|
public int Size { get; }
|
|
|
|
public PendingCopy(FenceHolder fence, int size)
|
|
{
|
|
Fence = fence;
|
|
Size = size;
|
|
fence.Get();
|
|
}
|
|
}
|
|
|
|
private readonly Queue<PendingCopy> _pendingCopies;
|
|
|
|
public StagingBuffer(VulkanRenderer gd, BufferManager bufferManager)
|
|
{
|
|
_gd = gd;
|
|
_buffer = bufferManager.Create(gd, BufferSize);
|
|
_pendingCopies = new Queue<PendingCopy>();
|
|
_freeSize = BufferSize;
|
|
}
|
|
|
|
public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
|
{
|
|
bool isRender = cbs != null;
|
|
CommandBufferScoped scoped = cbs ?? cbp.Rent();
|
|
|
|
// Must push all data to the buffer. If it can't fit, split it up.
|
|
|
|
endRenderPass?.Invoke();
|
|
|
|
while (data.Length > 0)
|
|
{
|
|
if (_freeSize < data.Length)
|
|
{
|
|
FreeCompleted();
|
|
}
|
|
|
|
while (_freeSize == 0)
|
|
{
|
|
if (!WaitFreeCompleted(cbp))
|
|
{
|
|
if (isRender)
|
|
{
|
|
_gd.FlushAllCommands();
|
|
scoped = cbp.Rent();
|
|
isRender = false;
|
|
}
|
|
else
|
|
{
|
|
scoped = cbp.ReturnAndRent(scoped);
|
|
}
|
|
}
|
|
}
|
|
|
|
int chunkSize = Math.Min(_freeSize, data.Length);
|
|
|
|
PushDataImpl(scoped, dst, dstOffset, data[..chunkSize]);
|
|
|
|
dstOffset += chunkSize;
|
|
data = data[chunkSize..];
|
|
}
|
|
|
|
if (!isRender)
|
|
{
|
|
scoped.Dispose();
|
|
}
|
|
}
|
|
|
|
private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
|
{
|
|
var srcBuffer = _buffer.GetBuffer();
|
|
var dstBuffer = dst.GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true);
|
|
|
|
int offset = _freeOffset;
|
|
int capacity = BufferSize - offset;
|
|
if (capacity < data.Length)
|
|
{
|
|
_buffer.SetDataUnchecked(offset, data[..capacity]);
|
|
_buffer.SetDataUnchecked(0, data[capacity..]);
|
|
|
|
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity);
|
|
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity);
|
|
}
|
|
else
|
|
{
|
|
_buffer.SetDataUnchecked(offset, data);
|
|
|
|
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length);
|
|
}
|
|
|
|
_freeOffset = (offset + data.Length) & (BufferSize - 1);
|
|
_freeSize -= data.Length;
|
|
Debug.Assert(_freeSize >= 0);
|
|
|
|
_pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length));
|
|
}
|
|
|
|
public bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
|
{
|
|
if (data.Length > BufferSize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_freeSize < data.Length)
|
|
{
|
|
FreeCompleted();
|
|
|
|
if (_freeSize < data.Length)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
endRenderPass();
|
|
|
|
PushDataImpl(cbs, dst, dstOffset, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool WaitFreeCompleted(CommandBufferPool cbp)
|
|
{
|
|
if (_pendingCopies.TryPeek(out var pc))
|
|
{
|
|
if (!pc.Fence.IsSignaled())
|
|
{
|
|
if (cbp.IsFenceOnRentedCommandBuffer(pc.Fence))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pc.Fence.Wait();
|
|
}
|
|
|
|
var dequeued = _pendingCopies.Dequeue();
|
|
Debug.Assert(dequeued.Fence == pc.Fence);
|
|
_freeSize += pc.Size;
|
|
pc.Fence.Put();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void FreeCompleted()
|
|
{
|
|
FenceHolder signalledFence = null;
|
|
while (_pendingCopies.TryPeek(out var pc) && (pc.Fence == signalledFence || pc.Fence.IsSignaled()))
|
|
{
|
|
signalledFence = pc.Fence; // Already checked - don't need to do it again.
|
|
var dequeued = _pendingCopies.Dequeue();
|
|
Debug.Assert(dequeued.Fence == pc.Fence);
|
|
_freeSize += pc.Size;
|
|
pc.Fence.Put();
|
|
}
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
_buffer.Dispose();
|
|
|
|
while (_pendingCopies.TryDequeue(out var pc))
|
|
{
|
|
pc.Fence.Put();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
}
|
|
}
|