forked from Mirror/Ryujinx
c8f9292bab
* drop split devices, rebase * add fallback to opengl if vulkan is not available * addressed review * ensure present image references are incremented and decremented when necessary * allow changing vsync for vulkan * fix screenshot on avalonia vulkan * save favorite when toggled * improve sync between popups * use separate devices for each new window * fix crash when closing window * addressed review * don't create the main window with immediate mode * change skia vk delegate to method * update vulkan throwonerror * addressed review
215 lines
7.1 KiB
C#
215 lines
7.1 KiB
C#
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()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
foreach (var usedCommandBuffer in _usedCommandBuffers)
|
|
{
|
|
usedCommandBuffer.Dispose();
|
|
}
|
|
|
|
_usedCommandBuffers.Clear();
|
|
}
|
|
}
|
|
|
|
private void DisposeCommandBuffer(VulkanCommandBuffer commandBuffer)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_usedCommandBuffers.Add(commandBuffer);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
FreeUsedCommandBuffers();
|
|
_device.Api.DestroyCommandPool(_device.InternalHandle, _commandPool, Span<AllocationCallbacks>.Empty);
|
|
}
|
|
}
|
|
|
|
public class VulkanCommandBuffer : IDisposable
|
|
{
|
|
private readonly VulkanCommandBufferPool _commandBufferPool;
|
|
private readonly VulkanDevice _device;
|
|
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)
|
|
{
|
|
_device = device;
|
|
_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;
|
|
}
|
|
|
|
lock (_lock)
|
|
{
|
|
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,
|
|
CommandBufferCount = 1,
|
|
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);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
_isDisposed = true;
|
|
|
|
_device.Api.WaitForFences(_device.InternalHandle, 1, _fence, true, ulong.MaxValue);
|
|
_device.Api.FreeCommandBuffers(_device.InternalHandle, _commandBufferPool._commandPool, 1, InternalHandle);
|
|
_device.Api.DestroyFence(_device.InternalHandle, _fence, Span<AllocationCallbacks>.Empty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|