using Silk.NET.Vulkan; using System.Collections.Generic; using System.Linq; namespace Ryujinx.Graphics.Vulkan { /// /// Holder for multiple host GPU fences. /// class MultiFenceHolder { private static int BufferUsageTrackingGranularity = 4096; private readonly Dictionary _fences; private BufferUsageBitmap _bufferUsageBitmap; /// /// Creates a new instance of the multiple fence holder. /// public MultiFenceHolder() { _fences = new Dictionary(); } /// /// Creates a new instance of the multiple fence holder, with a given buffer size in mind. /// /// Size of the buffer public MultiFenceHolder(int size) { _fences = new Dictionary(); _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); } /// /// Adds buffer usage information to the uses list. /// /// Index of the command buffer where the buffer is used /// Offset of the buffer being used /// Size of the buffer region being used, in bytes public void AddBufferUse(int cbIndex, int offset, int size) { _bufferUsageBitmap.Add(cbIndex, offset, size); } /// /// Removes all buffer usage information for a given command buffer. /// /// Index of the command buffer where the buffer is used public void RemoveBufferUses(int cbIndex) { _bufferUsageBitmap?.Clear(cbIndex); } /// /// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU. /// /// Index of the command buffer where the buffer is used /// Offset of the buffer being used /// Size of the buffer region being used, in bytes /// True if in use, false otherwise public bool IsBufferRangeInUse(int cbIndex, int offset, int size) { return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size); } /// /// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU. /// /// Offset of the buffer being used /// Size of the buffer region being used, in bytes /// True if in use, false otherwise public bool IsBufferRangeInUse(int offset, int size) { return _bufferUsageBitmap.OverlapsWith(offset, size); } /// /// Adds a fence to the holder. /// /// Command buffer index of the command buffer that owns the fence /// Fence to be added public void AddFence(int cbIndex, FenceHolder fence) { lock (_fences) { _fences.TryAdd(fence, cbIndex); } } /// /// Removes a fence from the holder. /// /// Command buffer index of the command buffer that owns the fence /// Fence to be removed public void RemoveFence(int cbIndex, FenceHolder fence) { lock (_fences) { _fences.Remove(fence); } } /// /// Wait until all the fences on the holder are signaled. /// /// Vulkan API instance /// GPU device that the fences belongs to public void WaitForFences(Vk api, Device device) { WaitForFencesImpl(api, device, 0, 0, false, 0UL); } /// /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. /// /// Vulkan API instance /// GPU device that the fences belongs to /// Start offset of the buffer range /// Size of the buffer range in bytes public void WaitForFences(Vk api, Device device, int offset, int size) { WaitForFencesImpl(api, device, offset, size, false, 0UL); } /// /// Wait until all the fences on the holder are signaled, or the timeout expires. /// /// Vulkan API instance /// GPU device that the fences belongs to /// Timeout in nanoseconds /// True if all fences were signaled, false otherwise public bool WaitForFences(Vk api, Device device, ulong timeout) { return WaitForFencesImpl(api, device, 0, 0, true, timeout); } /// /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. /// /// Vulkan API instance /// GPU device that the fences belongs to /// Start offset of the buffer range /// Size of the buffer range in bytes /// Indicates if should be used /// Timeout in nanoseconds /// True if all fences were signaled before the timeout expired, false otherwise private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout) { FenceHolder[] fenceHolders; Fence[] fences; lock (_fences) { fenceHolders = size != 0 ? GetOverlappingFences(offset, size) : _fences.Keys.ToArray(); fences = new Fence[fenceHolders.Length]; for (int i = 0; i < fenceHolders.Length; i++) { fences[i] = fenceHolders[i].Get(); } } if (fences.Length == 0) { return true; } bool signaled = true; if (hasTimeout) { signaled = FenceHelper.AllSignaled(api, device, fences, timeout); } else { FenceHelper.WaitAllIndefinitely(api, device, fences); } for (int i = 0; i < fenceHolders.Length; i++) { fenceHolders[i].Put(); } return signaled; } /// /// Gets fences to wait for use of a given buffer region. /// /// Offset of the range /// Size of the range in bytes /// Fences for the specified region private FenceHolder[] GetOverlappingFences(int offset, int size) { List overlapping = new List(); foreach (var kv in _fences) { var fence = kv.Key; var ownerCbIndex = kv.Value; if (_bufferUsageBitmap.OverlapsWith(ownerCbIndex, offset, size)) { overlapping.Add(fence); } } return overlapping.ToArray(); } } }