using Silk.NET.Vulkan; using System; namespace Ryujinx.Graphics.Vulkan { /// /// Holder for multiple host GPU fences. /// class MultiFenceHolder { private const int BufferUsageTrackingGranularity = 4096; private readonly FenceHolder[] _fences; private readonly BufferUsageBitmap _bufferUsageBitmap; /// /// Creates a new instance of the multiple fence holder. /// public MultiFenceHolder() { _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; } /// /// 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 FenceHolder[CommandBufferPool.MaxCommandBuffers]; _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); } /// /// Adds read/write 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 /// Whether the access is a write or not public void AddBufferUse(int cbIndex, int offset, int size, bool write) { _bufferUsageBitmap.Add(cbIndex, offset, size, false); if (write) { _bufferUsageBitmap.Add(cbIndex, offset, size, true); } } /// /// 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 only write usages should count /// True if in use, false otherwise public bool IsBufferRangeInUse(int offset, int size, bool write) { return _bufferUsageBitmap.OverlapsWith(offset, size, write); } /// /// Adds a fence to the holder. /// /// Command buffer index of the command buffer that owns the fence /// Fence to be added /// True if the command buffer's previous fence value was null public bool AddFence(int cbIndex, FenceHolder fence) { ref FenceHolder fenceRef = ref _fences[cbIndex]; if (fenceRef == null) { fenceRef = fence; return true; } return false; } /// /// Removes a fence from the holder. /// /// Command buffer index of the command buffer that owns the fence public void RemoveFence(int cbIndex) { _fences[cbIndex] = null; } /// /// Determines if a fence referenced on the given command buffer. /// /// Index of the command buffer to check if it's used /// True if referenced, false otherwise public bool HasFence(int cbIndex) { return _fences[cbIndex] != null; } /// /// 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) { Span fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders); Span fences = stackalloc Fence[count]; int fenceCount = 0; for (int i = 0; i < count; i++) { if (fenceHolders[i].TryGet(out Fence fence)) { fences[fenceCount] = fence; if (fenceCount < i) { fenceHolders[fenceCount] = fenceHolders[i]; } fenceCount++; } } if (fenceCount == 0) { return true; } bool signaled = true; if (hasTimeout) { signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout); } else { FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]); } for (int i = 0; i < fenceCount; i++) { fenceHolders[i].Put(); } return signaled; } /// /// Gets fences to wait for. /// /// Span to store fences in /// Number of fences placed in storage private int GetFences(Span storage) { int count = 0; for (int i = 0; i < _fences.Length; i++) { var fence = _fences[i]; if (fence != null) { storage[count++] = fence; } } return count; } /// /// Gets fences to wait for use of a given buffer region. /// /// Span to store overlapping fences in /// Offset of the range /// Size of the range in bytes /// Number of fences for the specified region placed in storage private int GetOverlappingFences(Span storage, int offset, int size) { int count = 0; for (int i = 0; i < _fences.Length; i++) { var fence = _fences[i]; if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size)) { storage[count++] = fence; } } return count; } } }