forked from MeloNX/MeloNX
* Initial implementation of buffer mirrors Generally slower right now, goal is to reduce render passes in games that do inline updates Fix support buffer mirrors Reintroduce vertex buffer mirror Add storage buffer support Optimisation part 1 More optimisation Avoid useless data copies. Remove unused cbIndex stuff Properly set write flag for storage buffers. Fix minor issues Not sure why this was here. Fix BufferRangeList Fix some big issues Align storage buffers rather than getting full buffer as a range Improves mirrorability of read-only storage buffers Increase staging buffer size, as it now contains mirrors Fix some issues with buffers not updating Fix buffer SetDataUnchecked offset for one of the paths when using mirrors Fix buffer mirrors interaction with buffer textures Fix mirror rebinding Move GetBuffer calls on indirect draws before BeginRenderPass to avoid draws without render pass Fix mirrors rebase Fix rebase 2023 * Fix crash when using stale vertex buffer Similar to `Get` with a size that's too large, just treat it as a clamp. * Explicitly set support buffer as mirrorable * Address feedback * Remove unused fragment of MVK workaround * Replace logging for staging buffer OOM * Address format issues * Address more format issues * Mini cleanup * Address more things * Rename BufferRangeList * Support bounding range for ClearMirrors and UploadPendingData * Add maximum size for vertex buffer mirrors * Enable index buffer mirrors Enabled on all platforms for the IbStreamer. * Feedback * Remove mystery BufferCache change Probably macos related? * Fix mirrors not creating when staging buffer is empty. * Change log level to debug
189 lines
5.6 KiB
C#
189 lines
5.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
|
|
namespace Ryujinx.Graphics.GAL.Multithreading
|
|
{
|
|
/// <summary>
|
|
/// Buffer handles given to the client are not the same as those provided by the backend,
|
|
/// as their handle is created at a later point on the queue.
|
|
/// The handle returned is a unique identifier that will map to the real buffer when it is available.
|
|
/// Note that any uses within the queue should be safe, but outside you must use MapBufferBlocking.
|
|
/// </summary>
|
|
class BufferMap
|
|
{
|
|
private ulong _bufferHandle = 0;
|
|
|
|
private readonly Dictionary<BufferHandle, BufferHandle> _bufferMap = new();
|
|
private readonly HashSet<BufferHandle> _inFlight = new();
|
|
private readonly AutoResetEvent _inFlightChanged = new(false);
|
|
|
|
internal BufferHandle CreateBufferHandle()
|
|
{
|
|
ulong handle64 = Interlocked.Increment(ref _bufferHandle);
|
|
|
|
BufferHandle threadedHandle = Unsafe.As<ulong, BufferHandle>(ref handle64);
|
|
|
|
lock (_inFlight)
|
|
{
|
|
_inFlight.Add(threadedHandle);
|
|
}
|
|
|
|
return threadedHandle;
|
|
}
|
|
|
|
internal void AssignBuffer(BufferHandle threadedHandle, BufferHandle realHandle)
|
|
{
|
|
lock (_bufferMap)
|
|
{
|
|
_bufferMap[threadedHandle] = realHandle;
|
|
}
|
|
|
|
lock (_inFlight)
|
|
{
|
|
_inFlight.Remove(threadedHandle);
|
|
}
|
|
|
|
_inFlightChanged.Set();
|
|
}
|
|
|
|
internal void UnassignBuffer(BufferHandle threadedHandle)
|
|
{
|
|
lock (_bufferMap)
|
|
{
|
|
_bufferMap.Remove(threadedHandle);
|
|
}
|
|
}
|
|
|
|
internal BufferHandle MapBuffer(BufferHandle handle)
|
|
{
|
|
// Maps a threaded buffer to a backend one.
|
|
// Threaded buffers are returned on creation as the buffer
|
|
// isn't actually created until the queue runs the command.
|
|
|
|
lock (_bufferMap)
|
|
{
|
|
if (!_bufferMap.TryGetValue(handle, out BufferHandle result))
|
|
{
|
|
result = BufferHandle.Null;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal BufferHandle MapBufferBlocking(BufferHandle handle)
|
|
{
|
|
// Blocks until the handle is available.
|
|
|
|
|
|
lock (_bufferMap)
|
|
{
|
|
if (_bufferMap.TryGetValue(handle, out BufferHandle result))
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
bool signal = false;
|
|
|
|
while (true)
|
|
{
|
|
lock (_inFlight)
|
|
{
|
|
if (!_inFlight.Contains(handle))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
_inFlightChanged.WaitOne();
|
|
signal = true;
|
|
}
|
|
|
|
if (signal)
|
|
{
|
|
// Signal other threads which might still be waiting.
|
|
_inFlightChanged.Set();
|
|
}
|
|
|
|
return MapBuffer(handle);
|
|
}
|
|
|
|
internal BufferRange MapBufferRange(BufferRange range)
|
|
{
|
|
return new BufferRange(MapBuffer(range.Handle), range.Offset, range.Size, range.Write);
|
|
}
|
|
|
|
internal Span<BufferRange> MapBufferRanges(Span<BufferRange> ranges)
|
|
{
|
|
// Rewrite the buffer ranges to point to the mapped handles.
|
|
|
|
lock (_bufferMap)
|
|
{
|
|
for (int i = 0; i < ranges.Length; i++)
|
|
{
|
|
ref BufferRange range = ref ranges[i];
|
|
|
|
if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result))
|
|
{
|
|
result = BufferHandle.Null;
|
|
}
|
|
|
|
range = new BufferRange(result, range.Offset, range.Size, range.Write);
|
|
}
|
|
}
|
|
|
|
return ranges;
|
|
}
|
|
|
|
internal Span<BufferAssignment> MapBufferRanges(Span<BufferAssignment> ranges)
|
|
{
|
|
// Rewrite the buffer ranges to point to the mapped handles.
|
|
|
|
lock (_bufferMap)
|
|
{
|
|
for (int i = 0; i < ranges.Length; i++)
|
|
{
|
|
ref BufferAssignment assignment = ref ranges[i];
|
|
BufferRange range = assignment.Range;
|
|
|
|
if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result))
|
|
{
|
|
result = BufferHandle.Null;
|
|
}
|
|
|
|
assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size, range.Write));
|
|
}
|
|
}
|
|
|
|
return ranges;
|
|
}
|
|
|
|
internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges)
|
|
{
|
|
// Rewrite the buffer ranges to point to the mapped handles.
|
|
|
|
lock (_bufferMap)
|
|
{
|
|
for (int i = 0; i < ranges.Length; i++)
|
|
{
|
|
BufferRange range = ranges[i].Buffer;
|
|
|
|
if (!_bufferMap.TryGetValue(range.Handle, out BufferHandle result))
|
|
{
|
|
result = BufferHandle.Null;
|
|
}
|
|
|
|
range = new BufferRange(result, range.Offset, range.Size, range.Write);
|
|
|
|
ranges[i] = new VertexBufferDescriptor(range, ranges[i].Stride, ranges[i].Divisor);
|
|
}
|
|
}
|
|
|
|
return ranges;
|
|
}
|
|
}
|
|
}
|