diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 0e3020e87..76ca078dd 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -370,7 +370,6 @@ namespace dxvk { // Return query and event handles m_gpuQueryTracker.reset(); - m_gpuEventTracker.reset(); // Less important stuff m_signalTracker.reset(); diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index d72914d73..cb9cc73f6 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -284,17 +284,10 @@ namespace dxvk { m_resources.trackSampler(sampler); } - /** - * \brief Tracks a GPU event - * - * The event will be returned to its event pool - * after the command buffer has finished executing. - * \param [in] handle Event handle - */ - void trackGpuEvent(DxvkGpuEventHandle handle) { - m_gpuEventTracker.trackEvent(handle); + void trackEvent(Rc&& event) { + m_resources.trackEvent(std::move(event)); } - + /** * \brief Tracks a GPU query * @@ -1082,7 +1075,6 @@ namespace dxvk { DxvkLifetimeTracker m_resources; DxvkSignalTracker m_signalTracker; - DxvkGpuEventTracker m_gpuEventTracker; DxvkGpuQueryTracker m_gpuQueryTracker; DxvkStatCounters m_statCounters; diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 98a061a48..0333b1534 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -2470,22 +2470,21 @@ namespace dxvk { void DxvkContext::signalGpuEvent(const Rc& event) { this->spillRenderPass(true); - DxvkGpuEventHandle handle = m_common->eventPool().allocEvent(); + Rc gpuEvent = m_common->eventPool().allocEvent(); + event->assignGpuEvent(gpuEvent); // Supported client APIs can't access device memory in a defined manner // without triggering a queue submission first, so we really only need // to wait for prior commands, especially queries, to complete. VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; - barrier.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; depInfo.memoryBarrierCount = 1; depInfo.pMemoryBarriers = &barrier; - m_cmd->cmdSetEvent(handle.event, &depInfo); - - m_cmd->trackGpuEvent(event->reset(handle)); - m_cmd->trackResource(event); + m_cmd->cmdSetEvent(gpuEvent->handle(), &depInfo); + m_cmd->trackEvent(std::move(gpuEvent)); } diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index fd5c208a6..23a8bbefb 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -139,7 +139,7 @@ namespace dxvk { Rc DxvkDevice::createGpuEvent() { - return new DxvkEvent(m_vkd); + return new DxvkEvent(this); } diff --git a/src/dxvk/dxvk_gpu_event.cpp b/src/dxvk/dxvk_gpu_event.cpp index 3913e58e5..2c8c228ab 100644 --- a/src/dxvk/dxvk_gpu_event.cpp +++ b/src/dxvk/dxvk_gpu_event.cpp @@ -3,34 +3,76 @@ namespace dxvk { - DxvkEvent::DxvkEvent(const Rc& vkd) - : m_vkd(vkd) { } + DxvkGpuEvent::DxvkGpuEvent( + DxvkGpuEventPool* parent) + : m_pool(parent) { + auto vk = m_pool->m_vkd; + VkEventCreateInfo info = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO }; + VkResult vr = vk->vkCreateEvent(vk->device(), &info, nullptr, &m_event); - DxvkEvent::~DxvkEvent() { - if (m_handle.pool && m_handle.event) - m_handle.pool->freeEvent(m_handle.event); + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create event: ", vr)); } - DxvkGpuEventStatus DxvkEvent::test() const { - if (!m_handle.event) + DxvkGpuEvent::~DxvkGpuEvent() { + auto vk = m_pool->m_vkd; + vk->vkDestroyEvent(vk->device(), m_event, nullptr); + } + + + void DxvkGpuEvent::free() { + m_pool->freeEvent(this); + } + + + + DxvkEvent::DxvkEvent(const Rc& device) + : m_device(device) { } + + + DxvkEvent::~DxvkEvent() { + + } + + + DxvkGpuEventStatus DxvkEvent::test() { + std::lock_guard lock(m_mutex); + + if (m_status == VK_EVENT_SET) + return DxvkGpuEventStatus::Signaled; + + if (!m_gpuEvent) 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; + + // Query current event status and recycle + // it as soon as a signal is observed. + auto vk = m_device->vkd(); + + m_status = vk->vkGetEventStatus( + vk->device(), m_gpuEvent->handle()); + + switch (m_status) { + case VK_EVENT_SET: + m_gpuEvent = nullptr; + return DxvkGpuEventStatus::Signaled; + + case VK_EVENT_RESET: + return DxvkGpuEventStatus::Pending; + + default: + return DxvkGpuEventStatus::Invalid; } } - DxvkGpuEventHandle DxvkEvent::reset(DxvkGpuEventHandle handle) { - m_vkd->vkResetEvent(m_vkd->device(), handle.event); - return std::exchange(m_handle, handle); + void DxvkEvent::assignGpuEvent( + Rc event) { + std::lock_guard lock(m_mutex); + + m_gpuEvent = std::move(event); + m_status = VK_NOT_READY; } @@ -41,61 +83,31 @@ namespace dxvk { DxvkGpuEventPool::~DxvkGpuEventPool() { - for (VkEvent ev : m_events) - m_vkd->vkDestroyEvent(m_vkd->device(), ev, nullptr); + for (auto e : m_freeEvents) + delete e; } - DxvkGpuEventHandle DxvkGpuEventPool::allocEvent() { - VkEvent event = VK_NULL_HANDLE; + Rc DxvkGpuEventPool::allocEvent() { + std::lock_guard lock(m_mutex); - { std::lock_guard lock(m_mutex); - - if (m_events.size() > 0) { - event = m_events.back(); - m_events.pop_back(); - } + Rc event; + + if (m_freeEvents.empty()) { + event = new DxvkGpuEvent(this); + } else { + event = m_freeEvents.back(); + m_freeEvents.pop_back(); } - if (!event) { - VkEventCreateInfo info = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO }; - - 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 }; + m_vkd->vkResetEvent(m_vkd->device(), event->handle()); + return 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(); + void DxvkGpuEventPool::freeEvent(DxvkGpuEvent* event) { + std::lock_guard lock(m_mutex); + m_freeEvents.push_back(event); } } \ No newline at end of file diff --git a/src/dxvk/dxvk_gpu_event.h b/src/dxvk/dxvk_gpu_event.h index 0d9b891e2..36dd494e3 100644 --- a/src/dxvk/dxvk_gpu_event.h +++ b/src/dxvk/dxvk_gpu_event.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "dxvk_resource.h" @@ -29,9 +30,50 @@ namespace dxvk { * as a pointer to the pool that the event * was allocated from. */ - struct DxvkGpuEventHandle { - DxvkGpuEventPool* pool = nullptr; - VkEvent event = VK_NULL_HANDLE; + class DxvkGpuEvent { + + public: + + explicit DxvkGpuEvent( + DxvkGpuEventPool* parent); + + ~DxvkGpuEvent(); + + /** + * \brief Increments ref count + */ + void incRef() { + m_refs.fetch_add(1u, std::memory_order_acquire); + } + + /** + * \brief Decrements ref count + * + * Returns event to the pool if no further + * references exist for this event. + */ + void decRef() { + if (m_refs.fetch_sub(1u, std::memory_order_release) == 1u) + free(); + } + + /** + * \brief Queries event handle + * \returns Event handle + */ + VkEvent handle() const { + return m_event; + } + + private: + + DxvkGpuEventPool* m_pool = nullptr; + VkEvent m_event = VK_NULL_HANDLE; + + std::atomic m_refs = { 0u }; + + void free(); + }; @@ -42,13 +84,29 @@ namespace dxvk { * the application to check whether a specific * command has completed execution. */ - class DxvkEvent : public DxvkResource { - + class DxvkEvent { + friend class DxvkContext; public: - DxvkEvent(const Rc& vkd); + DxvkEvent(const Rc& device); ~DxvkEvent(); + /** + * \brief Increments reference count + */ + force_inline void incRef() { + m_refCount.fetch_add(1u, std::memory_order_acquire); + } + + /** + * \brief Decrements reference count + * Frees the event as necessary. + */ + force_inline void decRef() { + if (m_refCount.fetch_sub(1u, std::memory_order_release) == 1u) + delete this; + } + /** * \brief Retrieves event status * @@ -56,24 +114,20 @@ namespace dxvk { * 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); + DxvkGpuEventStatus test(); private: - Rc m_vkd; - DxvkGpuEventHandle m_handle; + std::atomic m_refCount = { 0u }; + + sync::Spinlock m_mutex; + VkResult m_status = VK_NOT_READY; + + Rc m_device; + Rc m_gpuEvent; + + void assignGpuEvent( + Rc event); }; @@ -85,7 +139,7 @@ namespace dxvk { * a way to create and recycle Vulkan events. */ class DxvkGpuEventPool { - + friend class DxvkGpuEvent; public: DxvkGpuEventPool(const DxvkDevice* device); @@ -99,56 +153,22 @@ namespace dxvk { * state of the event is undefined. * \returns An event handle */ - DxvkGpuEventHandle allocEvent(); + Rc allocEvent(); /** * \brief Recycles an event * - * \param [in] handle Event to free + * \param [in] event Event object to free */ - void freeEvent(VkEvent event); + void freeEvent(DxvkGpuEvent* event); private: - Rc m_vkd; - dxvk::mutex m_mutex; - std::vector m_events; + Rc m_vkd; + + dxvk::mutex m_mutex; + std::vector m_freeEvents; }; - - /** - * \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/dxvk_lifetime.cpp b/src/dxvk/dxvk_lifetime.cpp index 2c09ec707..326b5af5d 100644 --- a/src/dxvk/dxvk_lifetime.cpp +++ b/src/dxvk/dxvk_lifetime.cpp @@ -10,6 +10,7 @@ namespace dxvk { m_resources.clear(); m_allocations.clear(); m_samplers.clear(); + m_events.clear(); } } \ No newline at end of file diff --git a/src/dxvk/dxvk_lifetime.h b/src/dxvk/dxvk_lifetime.h index cb6ff3f05..501198082 100644 --- a/src/dxvk/dxvk_lifetime.h +++ b/src/dxvk/dxvk_lifetime.h @@ -2,6 +2,7 @@ #include +#include "dxvk_gpu_event.h" #include "dxvk_resource.h" #include "dxvk_sampler.h" @@ -115,6 +116,14 @@ namespace dxvk { m_samplers.push_back(res); } + /** + * \brief Adds an event to track + * \param [in] res The event to track + */ + void trackEvent(Rc&& event) { + m_events.push_back(std::move(event)); + } + /** * \brief Adds a resource to track * \param [in] res The resource to track @@ -142,6 +151,7 @@ namespace dxvk { private: std::vector> m_samplers; + std::vector> m_events; std::vector> m_resources; std::vector> m_allocations;