1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-21 13:54:18 +01:00

[dxvk] Refactor events to be properly ref-counted

Mostly serves as a proof-of-concept for the pending query rework,
but also cleans up some silly code.
This commit is contained in:
Philip Rebohle 2024-10-12 18:17:46 +02:00 committed by Philip Rebohle
parent d00669cb52
commit 1b9c59c964
8 changed files with 180 additions and 147 deletions

View File

@ -370,7 +370,6 @@ namespace dxvk {
// Return query and event handles
m_gpuQueryTracker.reset();
m_gpuEventTracker.reset();
// Less important stuff
m_signalTracker.reset();

View File

@ -284,15 +284,8 @@ 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<DxvkGpuEvent>&& event) {
m_resources.trackEvent(std::move(event));
}
/**
@ -1082,7 +1075,6 @@ namespace dxvk {
DxvkLifetimeTracker m_resources;
DxvkSignalTracker m_signalTracker;
DxvkGpuEventTracker m_gpuEventTracker;
DxvkGpuQueryTracker m_gpuQueryTracker;
DxvkStatCounters m_statCounters;

View File

@ -2470,22 +2470,21 @@ namespace dxvk {
void DxvkContext::signalGpuEvent(const Rc<DxvkEvent>& event) {
this->spillRenderPass(true);
DxvkGpuEventHandle handle = m_common->eventPool().allocEvent();
Rc<DxvkGpuEvent> 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<DxvkAccess::None>(event);
m_cmd->cmdSetEvent(gpuEvent->handle(), &depInfo);
m_cmd->trackEvent(std::move(gpuEvent));
}

View File

@ -139,7 +139,7 @@ namespace dxvk {
Rc<DxvkEvent> DxvkDevice::createGpuEvent() {
return new DxvkEvent(m_vkd);
return new DxvkEvent(this);
}

View File

@ -3,34 +3,76 @@
namespace dxvk {
DxvkEvent::DxvkEvent(const Rc<vk::DeviceFn>& 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);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create event: ", vr));
}
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<DxvkDevice>& device)
: m_device(device) { }
DxvkEvent::~DxvkEvent() {
if (m_handle.pool && m_handle.event)
m_handle.pool->freeEvent(m_handle.event);
}
DxvkGpuEventStatus DxvkEvent::test() const {
if (!m_handle.event)
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);
// Query current event status and recycle
// it as soon as a signal is observed.
auto vk = m_device->vkd();
switch (status) {
case VK_EVENT_SET: return DxvkGpuEventStatus::Signaled;
case VK_EVENT_RESET: return DxvkGpuEventStatus::Pending;
default: return DxvkGpuEventStatus::Invalid;
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<DxvkGpuEvent> 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<DxvkGpuEvent> DxvkGpuEventPool::allocEvent() {
std::lock_guard lock(m_mutex);
{ std::lock_guard<dxvk::mutex> lock(m_mutex);
Rc<DxvkGpuEvent> event;
if (m_events.size() > 0) {
event = m_events.back();
m_events.pop_back();
}
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<dxvk::mutex> 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);
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <vector>
#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<uint32_t> 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<vk::DeviceFn>& vkd);
DxvkEvent(const Rc<DxvkDevice>& 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<vk::DeviceFn> m_vkd;
DxvkGpuEventHandle m_handle;
std::atomic<uint32_t> m_refCount = { 0u };
sync::Spinlock m_mutex;
VkResult m_status = VK_NOT_READY;
Rc<DxvkDevice> m_device;
Rc<DxvkGpuEvent> m_gpuEvent;
void assignGpuEvent(
Rc<DxvkGpuEvent> 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,55 +153,21 @@ namespace dxvk {
* state of the event is undefined.
* \returns An event handle
*/
DxvkGpuEventHandle allocEvent();
Rc<DxvkGpuEvent> 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<vk::DeviceFn> m_vkd;
dxvk::mutex m_mutex;
std::vector<VkEvent> 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<DxvkGpuEventHandle> m_handles;
std::vector<DxvkGpuEvent*> m_freeEvents;
};

View File

@ -10,6 +10,7 @@ namespace dxvk {
m_resources.clear();
m_allocations.clear();
m_samplers.clear();
m_events.clear();
}
}

View File

@ -2,6 +2,7 @@
#include <vector>
#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<DxvkGpuEvent>&& 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<Rc<DxvkSampler>> m_samplers;
std::vector<Rc<DxvkGpuEvent>> m_events;
std::vector<DxvkLifetime<DxvkResource>> m_resources;
std::vector<DxvkLifetime<DxvkResourceAllocation>> m_allocations;