mirror of
https://github.com/yuzu-emu/yuzu-android.git
synced 2025-06-17 01:38:42 -05:00
renderer_vulkan: Make unconditional use of VK_KHR_timeline_semaphore
This reworks how host<->device synchronization works on the Vulkan backend. Instead of "protecting" resources with a fence and signalling these as free when the fence is known to be signalled by the host GPU, use timeline semaphores. Vulkan timeline semaphores allow use to work on a subset of D3D12 fences. As far as we are concerned, timeline semaphores are a value set by the host or the device that can be waited by either of them. Taking advantange of this, we can have a monolithically increasing atomic value for each submission to the graphics queue. Instead of protecting resources with a fence, we simply store the current logical tick (the atomic value stored in CPU memory). When we want to know if a resource is free, it can be compared to the current GPU tick. This greatly simplifies resource management code and the free status of resources should have less false negatives. To workaround bugs in validation layers, when these are attached there's a thread waiting for timeline semaphores.
This commit is contained in:
@ -267,6 +267,7 @@ struct DeviceDispatch : public InstanceDispatch {
|
||||
PFN_vkGetFenceStatus vkGetFenceStatus;
|
||||
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
|
||||
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR;
|
||||
PFN_vkMapMemory vkMapMemory;
|
||||
PFN_vkQueueSubmit vkQueueSubmit;
|
||||
PFN_vkResetFences vkResetFences;
|
||||
@ -275,6 +276,7 @@ struct DeviceDispatch : public InstanceDispatch {
|
||||
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
|
||||
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||
PFN_vkWaitForFences vkWaitForFences;
|
||||
PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR;
|
||||
};
|
||||
|
||||
/// Loads instance agnostic function pointers.
|
||||
@ -550,7 +552,6 @@ using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>;
|
||||
using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
|
||||
using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>;
|
||||
using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>;
|
||||
using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>;
|
||||
using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>;
|
||||
using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>;
|
||||
|
||||
@ -582,7 +583,8 @@ public:
|
||||
/// Construct a queue handle.
|
||||
constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
|
||||
|
||||
VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept {
|
||||
VkResult Submit(Span<VkSubmitInfo> submit_infos,
|
||||
VkFence fence = VK_NULL_HANDLE) const noexcept {
|
||||
return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
|
||||
}
|
||||
|
||||
@ -674,6 +676,44 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> {
|
||||
using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle;
|
||||
|
||||
public:
|
||||
[[nodiscard]] u64 GetCounter() const {
|
||||
u64 value;
|
||||
Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value));
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a timeline semaphore on the host.
|
||||
*
|
||||
* @param value Value to wait
|
||||
* @param timeout Time in nanoseconds to timeout
|
||||
* @return True on successful wait, false on timeout
|
||||
*/
|
||||
bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const {
|
||||
const VkSemaphoreWaitInfoKHR wait_info{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.semaphoreCount = 1,
|
||||
.pSemaphores = &handle,
|
||||
.pValues = &value,
|
||||
};
|
||||
const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout);
|
||||
switch (result) {
|
||||
case VK_SUCCESS:
|
||||
return true;
|
||||
case VK_TIMEOUT:
|
||||
return false;
|
||||
default:
|
||||
throw Exception(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
|
||||
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
|
||||
|
||||
@ -694,6 +734,8 @@ public:
|
||||
|
||||
Semaphore CreateSemaphore() const;
|
||||
|
||||
Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
|
||||
|
||||
Fence CreateFence(const VkFenceCreateInfo& ci) const;
|
||||
|
||||
DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
|
||||
@ -721,7 +763,7 @@ public:
|
||||
|
||||
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
|
||||
|
||||
Event CreateNewEvent() const;
|
||||
Event CreateEvent() const;
|
||||
|
||||
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
|
||||
|
||||
|
Reference in New Issue
Block a user