Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan: Simplify MultiFenceHolder and managing them #4845

Merged
merged 6 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Ryujinx.Graphics.Vulkan/Auto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ public void AddCommandBufferDependencies(CommandBufferScoped cbs)
}
}

public bool TryIncrementReferenceCount()
{
int lastValue;
do
{
lastValue = _referenceCount;

if (lastValue == 0)
{
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);

return true;
}

public void IncrementReferenceCount()
{
if (Interlocked.Increment(ref _referenceCount) == 1)
Expand Down
5 changes: 3 additions & 2 deletions src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,10 @@ private unsafe bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, i
Auto<DisposableBuffer> dst,
int srcOffset,
int dstOffset,
int size)
int size,
bool registerSrcUsage = true)
{
var srcBuffer = src.Get(cbs, srcOffset, size).Value;
var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
var dstBuffer = dst.Get(cbs, dstOffset, size).Value;

InsertBufferBarrier(
Expand Down
14 changes: 8 additions & 6 deletions src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private struct ReservedCommandBuffer
public SemaphoreHolder Semaphore;

public List<IAuto> Dependants;
public HashSet<MultiFenceHolder> Waitables;
public List<MultiFenceHolder> Waitables;
public HashSet<SemaphoreHolder> Dependencies;

public void Initialize(Vk api, Device device, CommandPool pool)
Expand All @@ -47,7 +47,7 @@ public void Initialize(Vk api, Device device, CommandPool pool)
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);

Dependants = new List<IAuto>();
Waitables = new HashSet<MultiFenceHolder>();
Waitables = new List<MultiFenceHolder>();
Dependencies = new HashSet<SemaphoreHolder>();
}
}
Expand Down Expand Up @@ -143,8 +143,10 @@ public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
{
ref var entry = ref _commandBuffers[cbIndex];
waitable.AddFence(cbIndex, entry.Fence);
entry.Waitables.Add(waitable);
if (waitable.AddFence(cbIndex, entry.Fence))
{
entry.Waitables.Add(waitable);
}
}

public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size)
Expand All @@ -156,7 +158,7 @@ public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offs
ref var entry = ref _commandBuffers[i];

if (entry.InUse &&
entry.Waitables.Contains(waitable) &&
waitable.HasFence(i) &&
waitable.IsBufferRangeInUse(i, offset, size))
{
return true;
Expand Down Expand Up @@ -331,7 +333,7 @@ private void WaitAndDecrementRef(int cbIndex, bool refreshFence = true)

foreach (var waitable in entry.Waitables)
{
waitable.RemoveFence(cbIndex, entry.Fence);
waitable.RemoveFence(cbIndex);
waitable.RemoveBufferUses(cbIndex);
}

Expand Down
19 changes: 19 additions & 0 deletions src/Ryujinx.Graphics.Vulkan/FenceHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ public Fence GetUnsafe()
return _fence;
}

public bool TryGet(out Fence fence)
{
int lastValue;
do
{
lastValue = _referenceCount;

if (lastValue == 0)
{
fence = default;
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);

fence = _fence;
return true;
}

public Fence Get()
{
Interlocked.Increment(ref _referenceCount);
Expand Down
110 changes: 77 additions & 33 deletions src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Silk.NET.Vulkan;
using System;
using System.Buffers;
gdkchan marked this conversation as resolved.
Show resolved Hide resolved
using System.Collections.Generic;
using System.Linq;

Expand All @@ -11,15 +13,15 @@ class MultiFenceHolder
{
private static int BufferUsageTrackingGranularity = 4096;

private readonly Dictionary<FenceHolder, int> _fences;
private readonly FenceHolder[] _fences;
private BufferUsageBitmap _bufferUsageBitmap;

/// <summary>
/// Creates a new instance of the multiple fence holder.
/// </summary>
public MultiFenceHolder()
{
_fences = new Dictionary<FenceHolder, int>();
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
}

/// <summary>
Expand All @@ -28,7 +30,7 @@ public MultiFenceHolder()
/// <param name="size">Size of the buffer</param>
public MultiFenceHolder(int size)
{
_fences = new Dictionary<FenceHolder, int>();
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
}

Expand Down Expand Up @@ -80,25 +82,37 @@ public bool IsBufferRangeInUse(int offset, int size)
/// </summary>
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
/// <param name="fence">Fence to be added</param>
public void AddFence(int cbIndex, FenceHolder fence)
/// <returns>True if the command buffer's previous fence value was null</returns>
public bool AddFence(int cbIndex, FenceHolder fence)
{
lock (_fences)
ref FenceHolder fenceRef = ref _fences[cbIndex];

if (fenceRef == null)
{
_fences.TryAdd(fence, cbIndex);
fenceRef = fence;
return true;
}

return false;
}

/// <summary>
/// Removes a fence from the holder.
/// </summary>
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
/// <param name="fence">Fence to be removed</param>
public void RemoveFence(int cbIndex, FenceHolder fence)
public void RemoveFence(int cbIndex)
{
lock (_fences)
{
_fences.Remove(fence);
}
_fences[cbIndex] = null;
}

/// <summary>
/// Determines if a fence referenced on the given command buffer.
/// </summary>
/// <param name="cbIndex"></param>
gdkchan marked this conversation as resolved.
Show resolved Hide resolved
/// <returns></returns>
public bool HasFence(int cbIndex)
{
return _fences[cbIndex] != null;
}

/// <summary>
Expand Down Expand Up @@ -147,21 +161,29 @@ public bool WaitForFences(Vk api, Device device, ulong timeout)
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
{
FenceHolder[] fenceHolders;
Fence[] fences;
Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];

lock (_fences)
{
fenceHolders = size != 0 ? GetOverlappingFences(offset, size) : _fences.Keys.ToArray();
fences = new Fence[fenceHolders.Length];
int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
Span<Fence> fences = stackalloc Fence[count];

for (int i = 0; i < fenceHolders.Length; i++)
int fenceCount = 0;

for (int i = 0; i < count; i++)
{
if (fenceHolders[i].TryGet(out Fence fence))
{
fences[i] = fenceHolders[i].Get();
fences[fenceCount] = fence;

if (fenceCount < i)
{
fenceHolders[fenceCount] = fenceHolders[i];
}

fenceCount++;
}
}

if (fences.Length == 0)
if (fenceCount == 0)
{
return true;
}
Expand All @@ -170,43 +192,65 @@ private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool

if (hasTimeout)
{
signaled = FenceHelper.AllSignaled(api, device, fences, timeout);
signaled = FenceHelper.AllSignaled(api, device, fences.Slice(0, fenceCount), timeout);
}
else
{
FenceHelper.WaitAllIndefinitely(api, device, fences);
FenceHelper.WaitAllIndefinitely(api, device, fences.Slice(0, fenceCount));
}

for (int i = 0; i < fenceHolders.Length; i++)
for (int i = 0; i < fenceCount; i++)
{
fenceHolders[i].Put();
}

return signaled;
}

/// <summary>
/// Gets fences to wait for.
/// </summary>
/// <param name="storage">Span to store fences in</param>
/// <returns>Number of fences placed in storage</returns>
private int GetFences(Span<FenceHolder> storage)
{
int count = 0;

for (int i = 0; i < _fences.Length; i++)
{
var fence = _fences[i];

if (fence != null)
{
storage[count++] = fence;
}
}

return count;
}

/// <summary>
/// Gets fences to wait for use of a given buffer region.
/// </summary>
/// <param name="storage">Span to store overlapping fences in</param>
/// <param name="offset">Offset of the range</param>
/// <param name="size">Size of the range in bytes</param>
/// <returns>Fences for the specified region</returns>
private FenceHolder[] GetOverlappingFences(int offset, int size)
/// <returns>Number of fences for the specified region placed in storage</returns>
private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size)
{
List<FenceHolder> overlapping = new List<FenceHolder>();
int count = 0;

foreach (var kv in _fences)
for (int i = 0; i < _fences.Length; i++)
{
var fence = kv.Key;
var ownerCbIndex = kv.Value;
var fence = _fences[i];

if (_bufferUsageBitmap.OverlapsWith(ownerCbIndex, offset, size))
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
{
overlapping.Add(fence);
storage[count++] = fence;
}
}

return overlapping.ToArray();
return count;
}
}
}
14 changes: 12 additions & 2 deletions src/Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,26 @@ private BufferHolder ResizeIfNeeded(int size)
public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size)
{
var flushStorage = ResizeIfNeeded(size);
Auto<DisposableBuffer> srcBuffer;

using (var cbs = cbp.Rent())
{
var srcBuffer = buffer.GetBuffer(cbs.CommandBuffer);
srcBuffer = buffer.GetBuffer(cbs.CommandBuffer);
var dstBuffer = flushStorage.GetBuffer(cbs.CommandBuffer);

BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size);
if (srcBuffer.TryIncrementReferenceCount())
{
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
}
else
{
// Source buffer is no longer alive, don't copy anything to flush storage.
srcBuffer = null;
}
}

flushStorage.WaitForFences();
srcBuffer?.DecrementReferenceCount();
return flushStorage.GetDataStorage(0, size);
}

Expand Down