diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 5255be402..482e1d898 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -127,8 +127,14 @@ namespace dxvk { uint32_t index) { return new DxvkGpuQuery(m_vkd, type, flags, index); } - - + + + Rc DxvkDevice::createFence( + const DxvkFenceCreateInfo& fenceInfo) { + return new DxvkFence(this, fenceInfo); + } + + Rc DxvkDevice::createBuffer( const DxvkBufferCreateInfo& createInfo, VkMemoryPropertyFlags memoryType) { diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 89de0e6f5..d2096cdf2 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -6,6 +6,7 @@ #include "dxvk_constant_state.h" #include "dxvk_context.h" #include "dxvk_extensions.h" +#include "dxvk_fence.h" #include "dxvk_framebuffer.h" #include "dxvk_image.h" #include "dxvk_instance.h" @@ -269,6 +270,15 @@ namespace dxvk { VkQueryControlFlags flags, uint32_t index); + /** + * \brief Creates new fence + * + * \param [in] info Fence create info + * \returns The fence + */ + Rc createFence( + const DxvkFenceCreateInfo& fenceInfo); + /** * \brief Creates a buffer object * @@ -495,4 +505,4 @@ namespace dxvk { }; -} \ No newline at end of file +} diff --git a/src/dxvk/dxvk_fence.cpp b/src/dxvk/dxvk_fence.cpp new file mode 100644 index 000000000..2e5a362eb --- /dev/null +++ b/src/dxvk/dxvk_fence.cpp @@ -0,0 +1,95 @@ +#include "dxvk_device.h" +#include "dxvk_fence.h" + +namespace dxvk { + + DxvkFence::DxvkFence( + DxvkDevice* device, + const DxvkFenceCreateInfo& info) + : m_vkd(device->vkd()), m_info(info) { + VkSemaphoreTypeCreateInfo typeInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO }; + typeInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + typeInfo.initialValue = info.initialValue; + + VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &typeInfo }; + + VkResult vr = m_vkd->vkCreateSemaphore(m_vkd->device(), + &semaphoreInfo, nullptr, &m_semaphore); + + if (vr != VK_SUCCESS) + throw DxvkError("Failed to create timeline semaphore"); + + m_thread = dxvk::thread([this] { run(); }); + } + + + DxvkFence::~DxvkFence() { + m_stop.store(true); + m_thread.join(); + + m_vkd->vkDestroySemaphore(m_vkd->device(), m_semaphore, nullptr); + } + + + void DxvkFence::enqueueWait(uint64_t value, DxvkFenceEvent&& event) { + std::unique_lock lock(m_mutex); + + if (value > m_lastValue.load()) + m_queue.emplace(value, std::move(event)); + else + event(); + } + + + void DxvkFence::run() { + uint64_t value = 0ull; + + VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO }; + waitInfo.semaphoreCount = 1; + waitInfo.pSemaphores = &m_semaphore; + waitInfo.pValues = &value; + + while (!m_stop.load()) { + std::unique_lock lock(m_mutex); + + // Query actual semaphore value and start from there, so that + // we can skip over large increments in the semaphore value + VkResult vr = m_vkd->vkGetSemaphoreCounterValue(m_vkd->device(), m_semaphore, &value); + + if (vr != VK_SUCCESS) { + Logger::err(str::format("Failed to query semaphore value: ", vr)); + return; + } + + m_lastValue.store(value); + + // Signal all enqueued events whose value is not greater than + // the current semaphore value + while (!m_queue.empty() && m_queue.top().value <= value) { + m_queue.top().event(); + m_queue.pop(); + } + + if (m_stop) + return; + + lock.unlock(); + + // Wait for the semaphore to be singaled again and update state. + // The timeout is unfortunate, but we can't always know when a + // signal operation has been recorded, and the alternative would + // be to create a teardown semaphore and use WAIT_ANY, which may + // be fall back to a busy-wait loop on some drivers. + value += 1; + + vr = m_vkd->vkWaitSemaphores( + m_vkd->device(), &waitInfo, 10'000'000ull); + + if (vr != VK_SUCCESS && vr != VK_TIMEOUT) { + Logger::err(str::format("Failed to wait for semaphore: ", vr)); + return; + } + } + } + +} diff --git a/src/dxvk/dxvk_fence.h b/src/dxvk/dxvk_fence.h new file mode 100644 index 000000000..a533d22ba --- /dev/null +++ b/src/dxvk/dxvk_fence.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include + +#include "dxvk_resource.h" + +#include "../util/thread.h" + +namespace dxvk { + + class DxvkDevice; + + using DxvkFenceEvent = std::function; + + /** + * \brief Fence create info + */ + struct DxvkFenceCreateInfo { + uint64_t initialValue; + }; + + /** + * \brief Fence + * + * Wrapper around Vulkan timeline semaphores that + * can signal an event when the value changes. + */ + class DxvkFence : public RcObject { + + public: + + DxvkFence( + DxvkDevice* device, + const DxvkFenceCreateInfo& info); + + ~DxvkFence(); + + /** + * \brief Semaphore handle + */ + VkSemaphore handle() const { + return m_semaphore; + } + + /** + * \brief Retrieves current semaphore value + * \returns Current semaphore value + */ + uint64_t getValue() { + return m_lastValue.load(); + } + + /** + * \brief Enqueues semaphore wait + * + * Signals the given event when the + * semaphore reaches the given value. + * \param [in] value Enqueue value + * \param [in] event Callback + */ + void enqueueWait(uint64_t value, DxvkFenceEvent&& event); + + private: + + struct QueueItem { + QueueItem() { } + QueueItem(uint32_t v, DxvkFenceEvent&& e) + : value(v), event(std::move(e)) { } + + uint64_t value; + DxvkFenceEvent event; + + bool operator == (const QueueItem& item) const { return value == item.value; } + bool operator != (const QueueItem& item) const { return value != item.value; } + bool operator < (const QueueItem& item) const { return value < item.value; } + bool operator <= (const QueueItem& item) const { return value <= item.value; } + bool operator > (const QueueItem& item) const { return value > item.value; } + bool operator >= (const QueueItem& item) const { return value >= item.value; } + }; + + Rc m_vkd; + DxvkFenceCreateInfo m_info; + VkSemaphore m_semaphore; + + std::priority_queue m_queue; + std::atomic m_lastValue = { 0ull }; + std::atomic m_stop = { false }; + + dxvk::mutex m_mutex; + dxvk::thread m_thread; + + void run(); + + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index b82be6aac..99100b32d 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -73,6 +73,7 @@ dxvk_src = files([ 'dxvk_device.cpp', 'dxvk_device_filter.cpp', 'dxvk_extensions.cpp', + 'dxvk_fence.cpp', 'dxvk_format.cpp', 'dxvk_framebuffer.cpp', 'dxvk_gpu_event.cpp',