[dxvk] Add DxvkFence

Co-authored-by: Derek Lesho <dlesho@codeweavers.com>
This commit is contained in:
Philip Rebohle 2021-10-22 01:04:53 +02:00
parent a40724aaf8
commit 446ec07f3b
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
5 changed files with 212 additions and 3 deletions

View File

@ -127,8 +127,14 @@ namespace dxvk {
uint32_t index) {
return new DxvkGpuQuery(m_vkd, type, flags, index);
}
Rc<DxvkFence> DxvkDevice::createFence(
const DxvkFenceCreateInfo& fenceInfo) {
return new DxvkFence(this, fenceInfo);
}
Rc<DxvkBuffer> DxvkDevice::createBuffer(
const DxvkBufferCreateInfo& createInfo,
VkMemoryPropertyFlags memoryType) {

View File

@ -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<DxvkFence> createFence(
const DxvkFenceCreateInfo& fenceInfo);
/**
* \brief Creates a buffer object
*
@ -495,4 +505,4 @@ namespace dxvk {
};
}
}

95
src/dxvk/dxvk_fence.cpp Normal file
View File

@ -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<dxvk::mutex> 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<dxvk::mutex> 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;
}
}
}
}

97
src/dxvk/dxvk_fence.h Normal file
View File

@ -0,0 +1,97 @@
#pragma once
#include <functional>
#include <queue>
#include "dxvk_resource.h"
#include "../util/thread.h"
namespace dxvk {
class DxvkDevice;
using DxvkFenceEvent = std::function<void ()>;
/**
* \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<vk::DeviceFn> m_vkd;
DxvkFenceCreateInfo m_info;
VkSemaphore m_semaphore;
std::priority_queue<QueueItem> m_queue;
std::atomic<uint64_t> m_lastValue = { 0ull };
std::atomic<bool> m_stop = { false };
dxvk::mutex m_mutex;
dxvk::thread m_thread;
void run();
};
}

View File

@ -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',