using System;
using System.Collections.Generic;
using Silk.NET.Vulkan;
namespace Ryujinx.Ava.Ui.Vulkan
{
internal class VulkanCommandBufferPool : IDisposable
private readonly VulkanDevice _device;
private readonly CommandPool _commandPool;
private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new();
private readonly object _lock = new object();
public unsafe VulkanCommandBufferPool(VulkanDevice device, VulkanPhysicalDevice physicalDevice)
_device = device;
var commandPoolCreateInfo = new CommandPoolCreateInfo
SType = StructureType.CommandPoolCreateInfo,
Flags = CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit,
QueueFamilyIndex = physicalDevice.QueueFamilyIndex
};
device.Api.CreateCommandPool(_device.InternalHandle, commandPoolCreateInfo, null, out _commandPool)
.ThrowOnError();
}
private CommandBuffer AllocateCommandBuffer()
var commandBufferAllocateInfo = new CommandBufferAllocateInfo
SType = StructureType.CommandBufferAllocateInfo,
CommandPool = _commandPool,
CommandBufferCount = 1,
Level = CommandBufferLevel.Primary
lock (_lock)
_device.Api.AllocateCommandBuffers(_device.InternalHandle, commandBufferAllocateInfo, out var commandBuffer);
return commandBuffer;
public VulkanCommandBuffer CreateCommandBuffer()
return new(_device, this);
public void FreeUsedCommandBuffers()
foreach (var usedCommandBuffer in _usedCommandBuffers)
usedCommandBuffer.Dispose();
_usedCommandBuffers.Clear();
private void DisposeCommandBuffer(VulkanCommandBuffer commandBuffer)
_usedCommandBuffers.Add(commandBuffer);
public void Dispose()
FreeUsedCommandBuffers();
_device.Api.DestroyCommandPool(_device.InternalHandle, _commandPool, Span<AllocationCallbacks>.Empty);
public class VulkanCommandBuffer : IDisposable
private readonly VulkanCommandBufferPool _commandBufferPool;
private readonly Fence _fence;
private bool _hasEnded;
private bool _hasStarted;
private bool _isDisposed;
private object _lock = new object();
public IntPtr Handle => InternalHandle.Handle;
internal CommandBuffer InternalHandle { get; }
internal unsafe VulkanCommandBuffer(VulkanDevice device, VulkanCommandBufferPool commandBufferPool)
_commandBufferPool = commandBufferPool;
InternalHandle = _commandBufferPool.AllocateCommandBuffer();
var fenceCreateInfo = new FenceCreateInfo()
SType = StructureType.FenceCreateInfo,
Flags = FenceCreateFlags.FenceCreateSignaledBit
device.Api.CreateFence(device.InternalHandle, fenceCreateInfo, null, out _fence);
public void WaitForFence()
if (_isDisposed)
return;
if (!_isDisposed)
_device.Api.WaitForFences(_device.InternalHandle, 1, _fence, true, ulong.MaxValue);
public void BeginRecording()
if (!_hasStarted)
_hasStarted = true;
var beginInfo = new CommandBufferBeginInfo
SType = StructureType.CommandBufferBeginInfo,
Flags = CommandBufferUsageFlags.CommandBufferUsageOneTimeSubmitBit
_device.Api.BeginCommandBuffer(InternalHandle, beginInfo);
public void EndRecording()
if (_hasStarted && !_hasEnded)
_hasEnded = true;
_device.Api.EndCommandBuffer(InternalHandle);
public void Submit()
Submit(null, null, null, _fence);
public unsafe void Submit(
ReadOnlySpan<Semaphore> waitSemaphores,
ReadOnlySpan<PipelineStageFlags> waitDstStageMask,
ReadOnlySpan<Semaphore> signalSemaphores,
Fence? fence = null)
EndRecording();
if (!fence.HasValue)
fence = _fence;
fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask)
var commandBuffer = InternalHandle;
var submitInfo = new SubmitInfo
SType = StructureType.SubmitInfo,
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
PWaitSemaphores = pWaitSemaphores,
PWaitDstStageMask = pWaitDstStageMask,
PCommandBuffers = &commandBuffer,
SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0,
PSignalSemaphores = pSignalSemaphores,
_device.Api.ResetFences(_device.InternalHandle, 1, fence.Value);
_device.Submit(submitInfo, fence.Value);
_commandBufferPool.DisposeCommandBuffer(this);
_isDisposed = true;
_device.Api.FreeCommandBuffers(_device.InternalHandle, _commandBufferPool._commandPool, 1, InternalHandle);
_device.Api.DestroyFence(_device.InternalHandle, _fence, Span<AllocationCallbacks>.Empty);