From 4da89ccc48691e4692770efcf766bde79c89a0d1 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 16 Nov 2018 23:40:24 +0100 Subject: [PATCH] [dxvk] Add GPU event class GPU events allow for finer-grained CPU<>GPU synchronization than the current approach, so we should change our implementation. --- src/dxvk/dxvk_gpu_event.cpp | 103 ++++++++++++++++++++++++ src/dxvk/dxvk_gpu_event.h | 153 ++++++++++++++++++++++++++++++++++++ src/dxvk/meson.build | 1 + 3 files changed, 257 insertions(+) create mode 100644 src/dxvk/dxvk_gpu_event.cpp create mode 100644 src/dxvk/dxvk_gpu_event.h diff --git a/src/dxvk/dxvk_gpu_event.cpp b/src/dxvk/dxvk_gpu_event.cpp new file mode 100644 index 00000000..e2dac1c4 --- /dev/null +++ b/src/dxvk/dxvk_gpu_event.cpp @@ -0,0 +1,103 @@ +#include "dxvk_gpu_event.h" + +namespace dxvk { + + DxvkGpuEvent::DxvkGpuEvent(const Rc& vkd) + : m_vkd(vkd) { } + + + DxvkGpuEvent::~DxvkGpuEvent() { + if (m_handle.pool && m_handle.event) + m_handle.pool->freeEvent(m_handle.event); + } + + + DxvkGpuEventStatus DxvkGpuEvent::test() const { + if (!m_handle.event) + return DxvkGpuEventStatus::Invalid; + + VkResult status = m_vkd->vkGetEventStatus( + m_vkd->device(), m_handle.event); + + switch (status) { + case VK_EVENT_SET: return DxvkGpuEventStatus::Signaled; + case VK_EVENT_RESET: return DxvkGpuEventStatus::Pending; + default: return DxvkGpuEventStatus::Invalid; + } + } + + + DxvkGpuEventHandle DxvkGpuEvent::reset(DxvkGpuEventHandle handle) { + m_vkd->vkResetEvent(m_vkd->device(), handle.event); + return std::exchange(m_handle, handle); + } + + + + + DxvkGpuEventPool::DxvkGpuEventPool(const Rc& vkd) + : m_vkd(vkd) { } + + + DxvkGpuEventPool::~DxvkGpuEventPool() { + for (VkEvent ev : m_events) + m_vkd->vkDestroyEvent(m_vkd->device(), ev, nullptr); + } + + + DxvkGpuEventHandle DxvkGpuEventPool::allocEvent() { + VkEvent event = VK_NULL_HANDLE; + + { std::lock_guard lock(m_mutex); + + if (m_events.size() > 0) { + event = m_events.back(); + m_events.pop_back(); + } + } + + if (!event) { + VkEventCreateInfo info; + info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + + VkResult status = m_vkd->vkCreateEvent( + m_vkd->device(), &info, nullptr, &event); + + if (status != VK_SUCCESS) { + Logger::err("DXVK: Failed to create GPU event"); + return DxvkGpuEventHandle(); + } + } + + return { this, event }; + } + + + void DxvkGpuEventPool::freeEvent(VkEvent event) { + std::lock_guard lock(m_mutex); + m_events.push_back(event); + } + + + + + DxvkGpuEventTracker::DxvkGpuEventTracker() { } + DxvkGpuEventTracker::~DxvkGpuEventTracker() { } + + + void DxvkGpuEventTracker::trackEvent(DxvkGpuEventHandle handle) { + if (handle.pool && handle.event) + m_handles.push_back(handle); + } + + + void DxvkGpuEventTracker::reset() { + for (const auto& h : m_handles) + h.pool->freeEvent(h.event); + + m_handles.clear(); + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_gpu_event.h b/src/dxvk/dxvk_gpu_event.h new file mode 100644 index 00000000..b52e9f4d --- /dev/null +++ b/src/dxvk/dxvk_gpu_event.h @@ -0,0 +1,153 @@ +#pragma once + +#include + +#include "dxvk_resource.h" + +namespace dxvk { + + class DxvkGpuEventPool; + + /** + * \brief Event status + * + * Reports whether the event is in + * a signaled or unsignaled state. + */ + enum class DxvkGpuEventStatus : uint32_t { + Invalid = 0, + Pending = 1, + Signaled = 2, + }; + + + /** + * \brief Event handle + * + * Stores the event handle itself as well + * as a pointer to the pool that the event + * was allocated from. + */ + struct DxvkGpuEventHandle { + DxvkGpuEventPool* pool = nullptr; + VkEvent event = VK_NULL_HANDLE; + }; + + + /** + * \brief GPU event + * + * An event managed by the GPU which allows + * the application to check whether a specific + * command has completed execution. + */ + class DxvkGpuEvent : public DxvkResource { + + public: + + DxvkGpuEvent(const Rc& vkd); + ~DxvkGpuEvent(); + + /** + * \brief Retrieves event status + * + * Only valid after the event has been + * recorded intro a command buffer. + * \returns Event status + */ + DxvkGpuEventStatus test() const; + + /** + * \brief Resets event + * + * Assigns a new Vulkan event to this event + * object and replaces the old one. The old + * event should be freed as soon as the GPU + * stops using it. + * \param [in] handle New GPU event handle + * \returns Old GPU event handle + */ + DxvkGpuEventHandle reset(DxvkGpuEventHandle handle); + + private: + + Rc m_vkd; + DxvkGpuEventHandle m_handle; + + }; + + + /** + * \brief Event pool + * + * Thread-safe event allocator that provides + * a way to create and recycle Vulkan events. + */ + class DxvkGpuEventPool : public RcObject { + + public: + + DxvkGpuEventPool(const Rc& vkd); + ~DxvkGpuEventPool(); + + /** + * \brief Allocates an event + * + * Either returns a recycled event, or + * creates a new one if necessary. The + * state of the event is undefined. + * \returns An event handle + */ + DxvkGpuEventHandle allocEvent(); + + /** + * \brief Recycles an event + * + * \param [in] handle Event to free + */ + void freeEvent(VkEvent event); + + private: + + Rc m_vkd; + sync::Spinlock m_mutex; + std::vector m_events; + + }; + + + /** + * \brief GPU event tracker + * + * Stores events currently accessed by the + * GPU, and returns them to the event pool + * once they are no longer in use. + */ + class DxvkGpuEventTracker { + + public: + + DxvkGpuEventTracker(); + ~DxvkGpuEventTracker(); + + /** + * \brief Tracks an event + * \param [in] handle Event to track + */ + void trackEvent(DxvkGpuEventHandle handle); + + /** + * \brief Resets event tracker + * + * Releases all tracked events back + * to the respective event pool + */ + void reset(); + + private: + + std::vector m_handles; + + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 5c453036..b50901e5 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -55,6 +55,7 @@ dxvk_src = files([ 'dxvk_event.cpp', 'dxvk_format.cpp', 'dxvk_framebuffer.cpp', + 'dxvk_gpu_event.cpp', 'dxvk_graphics.cpp', 'dxvk_image.cpp', 'dxvk_instance.cpp',