From 693ee7898d3a94f6fb763e56d339baad547b78e9 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 12 Oct 2024 21:45:09 +0200 Subject: [PATCH] [dxvk] Refactor queries to be properly ref-counted Should fix a long-standing lifetime tracking issue with timestamp queries as well as invalid Vulkan usage with overlapping scoped queries. --- src/dxvk/dxvk_cmdlist.cpp | 3 - src/dxvk/dxvk_cmdlist.h | 14 +- src/dxvk/dxvk_device.cpp | 2 +- src/dxvk/dxvk_gpu_query.cpp | 355 ++++++++++++++++++------------------ src/dxvk/dxvk_gpu_query.h | 261 +++++++++++++------------- src/dxvk/dxvk_lifetime.cpp | 1 + src/dxvk/dxvk_lifetime.h | 10 + 7 files changed, 323 insertions(+), 323 deletions(-) diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 76ca078dd..5290e4624 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -368,9 +368,6 @@ namespace dxvk { // that are no longer in use m_resources.reset(); - // Return query and event handles - m_gpuQueryTracker.reset(); - // Less important stuff m_signalTracker.reset(); m_statCounters.reset(); diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index cb9cc73f6..a25c4fa43 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -288,17 +288,10 @@ namespace dxvk { m_resources.trackEvent(std::move(event)); } - /** - * \brief Tracks a GPU query - * - * The query handle will be returned to its allocator - * after the command buffer has finished executing. - * \param [in] handle Event handle - */ - void trackGpuQuery(DxvkGpuQueryHandle handle) { - m_gpuQueryTracker.trackQuery(handle); + void trackQuery(Rc&& query) { + m_resources.trackQuery(std::move(query)); } - + /** * \brief Tracks a graphics pipeline * \param [in] pipeline Pipeline @@ -1075,7 +1068,6 @@ namespace dxvk { DxvkLifetimeTracker m_resources; DxvkSignalTracker m_signalTracker; - DxvkGpuQueryTracker m_gpuQueryTracker; DxvkStatCounters m_statCounters; DxvkCommandSubmission m_commandSubmission; diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 23a8bbefb..4bc1ea6ba 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -147,7 +147,7 @@ namespace dxvk { VkQueryType type, VkQueryControlFlags flags, uint32_t index) { - return new DxvkQuery(m_vkd, type, flags, index); + return new DxvkQuery(this, type, flags, index); } diff --git a/src/dxvk/dxvk_gpu_query.cpp b/src/dxvk/dxvk_gpu_query.cpp index 1f32629e6..7040b53d9 100644 --- a/src/dxvk/dxvk_gpu_query.cpp +++ b/src/dxvk/dxvk_gpu_query.cpp @@ -1,4 +1,4 @@ -#include +#include #include "dxvk_cmdlist.h" #include "dxvk_device.h" @@ -6,25 +6,25 @@ namespace dxvk { + void DxvkGpuQuery::free() { + m_allocator->freeQuery(this); + } + + + + DxvkQuery::DxvkQuery( - const Rc& vkd, - VkQueryType type, - VkQueryControlFlags flags, - uint32_t index) - : m_vkd(vkd), m_type(type), m_flags(flags), - m_index(index), m_ended(false) { - + const Rc& device, + VkQueryType type, + VkQueryControlFlags flags, + uint32_t index) + : m_device(device), m_type(type), m_flags(flags), m_index(index) { + } DxvkQuery::~DxvkQuery() { - for (size_t i = 0; i < m_handles.size(); i++) - m_handles[i].allocator->freeQuery(m_handles[i]); - } - - bool DxvkQuery::isIndexed() const { - return m_type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT; } @@ -34,12 +34,14 @@ namespace dxvk { // Callers must ensure that no begin call is pending when // calling this. Given that, once the query is ended, we // know that no other thread will access query state. - if (!m_ended.load(std::memory_order_acquire)) + std::lock_guard lock(m_mutex); + + if (!m_ended) return DxvkGpuQueryStatus::Invalid; // Accumulate query data from all available queries - DxvkGpuQueryStatus status = this->accumulateQueryData(); - + DxvkGpuQueryStatus status = accumulateQueryDataLocked(); + // Treat non-precise occlusion queries as available // if we already know the result will be non-zero if ((status == DxvkGpuQueryStatus::Pending) @@ -56,64 +58,61 @@ namespace dxvk { } - void DxvkQuery::begin(const Rc& cmd) { - // Not useful to enforce a memory order here since - // only the false->true transition is defined. - m_ended.store(false, std::memory_order_relaxed); - - // Ideally we should have no queries left at this point, - // if we do, lifetime-track them with the command list. - for (size_t i = 0; i < m_handles.size(); i++) - cmd->trackGpuQuery(m_handles[i]); - - m_handles.clear(); - - // Reset accumulated query data - m_queryData = DxvkQueryData(); + void DxvkQuery::begin() { + std::lock_guard lock(m_mutex); + m_queries.clear(); + m_queryData = { }; + m_ended = false; } - + void DxvkQuery::end() { - // Ensure that all prior writes are made available - m_ended.store(true, std::memory_order_release); + std::lock_guard lock(m_mutex); + m_ended = true; } - void DxvkQuery::addQueryHandle(const DxvkGpuQueryHandle& handle) { + void DxvkQuery::addGpuQuery(Rc query) { // Already accumulate available queries here in case // we already allocated a large number of queries - if (m_handles.size() >= m_handles.MinCapacity) - this->accumulateQueryData(); + std::lock_guard lock(m_mutex); - m_handles.push_back(handle); + if (m_queries.size() >= m_queries.MinCapacity) + accumulateQueryDataLocked(); + + m_queries.push_back(std::move(query)); } - DxvkGpuQueryStatus DxvkQuery::accumulateQueryDataForHandle( - const DxvkGpuQueryHandle& handle) { + DxvkGpuQueryStatus DxvkQuery::accumulateQueryDataForGpuQueryLocked( + const Rc& query) { + auto vk = m_device->vkd(); + DxvkQueryData tmpData = { }; // Try to copy query data to temporary structure - VkResult result = m_vkd->vkGetQueryPoolResults(m_vkd->device(), - handle.queryPool, handle.queryId, 1, + std::pair handle = query->getQuery(); + + VkResult result = vk->vkGetQueryPoolResults( + vk->device(), handle.first, handle.second, 1, sizeof(DxvkQueryData), &tmpData, sizeof(DxvkQueryData), VK_QUERY_RESULT_64_BIT); - + if (result == VK_NOT_READY) return DxvkGpuQueryStatus::Pending; else if (result != VK_SUCCESS) return DxvkGpuQueryStatus::Failed; - + // Add numbers to the destination structure switch (m_type) { case VK_QUERY_TYPE_OCCLUSION: m_queryData.occlusion.samplesPassed += tmpData.occlusion.samplesPassed; break; - + case VK_QUERY_TYPE_TIMESTAMP: m_queryData.timestamp.time = tmpData.timestamp.time; break; - + case VK_QUERY_TYPE_PIPELINE_STATISTICS: m_queryData.statistic.iaVertices += tmpData.statistic.iaVertices; m_queryData.statistic.iaPrimitives += tmpData.statistic.iaPrimitives; @@ -142,7 +141,7 @@ namespace dxvk { } - DxvkGpuQueryStatus DxvkQuery::accumulateQueryData() { + DxvkGpuQueryStatus DxvkQuery::accumulateQueryDataLocked() { DxvkGpuQueryStatus status = DxvkGpuQueryStatus::Available; // Process available queries and return them to the @@ -150,8 +149,8 @@ namespace dxvk { // number of Vulkan queries in flight. size_t queriesAvailable = 0; - while (queriesAvailable < m_handles.size()) { - status = this->accumulateQueryDataForHandle(m_handles[queriesAvailable]); + while (queriesAvailable < m_queries.size()) { + status = accumulateQueryDataForGpuQueryLocked(m_queries[queriesAvailable]); if (status != DxvkGpuQueryStatus::Available) break; @@ -160,13 +159,10 @@ namespace dxvk { } if (queriesAvailable) { - for (size_t i = 0; i < queriesAvailable; i++) - m_handles[i].allocator->freeQuery(m_handles[i]); + for (size_t i = queriesAvailable; i < m_queries.size(); i++) + m_queries[i - queriesAvailable] = m_queries[i]; - for (size_t i = queriesAvailable; i < m_handles.size(); i++) - m_handles[i - queriesAvailable] = m_handles[i]; - - m_handles.resize(m_handles.size() - queriesAvailable); + m_queries.resize(m_queries.size() - queriesAvailable); } return status; @@ -176,11 +172,10 @@ namespace dxvk { DxvkGpuQueryAllocator::DxvkGpuQueryAllocator( - DxvkDevice* device, - VkQueryType queryType, - uint32_t queryPoolSize) + DxvkDevice* device, + VkQueryType queryType, + uint32_t queryPoolSize) : m_device (device), - m_vkd (device->vkd()), m_queryType (queryType), m_queryPoolSize (queryPoolSize) { @@ -188,35 +183,34 @@ namespace dxvk { DxvkGpuQueryAllocator::~DxvkGpuQueryAllocator() { - for (VkQueryPool pool : m_pools) { - m_vkd->vkDestroyQueryPool( - m_vkd->device(), pool, nullptr); + auto vk = m_device->vkd(); + + for (auto& p : m_pools) { + vk->vkDestroyQueryPool(vk->device(), p.pool, nullptr); + delete[] p.queries; } } - DxvkGpuQueryHandle DxvkGpuQueryAllocator::allocQuery() { + Rc DxvkGpuQueryAllocator::allocQuery() { std::lock_guard lock(m_mutex); - if (m_handles.size() == 0) - this->createQueryPool(); + if (!m_free) + createQueryPool(); - if (m_handles.size() == 0) - return DxvkGpuQueryHandle(); - - DxvkGpuQueryHandle result = m_handles.back(); - m_handles.pop_back(); - return result; + return std::exchange(m_free, m_free->m_next); } - void DxvkGpuQueryAllocator::freeQuery(DxvkGpuQueryHandle handle) { + void DxvkGpuQueryAllocator::freeQuery(DxvkGpuQuery* query) { std::lock_guard lock(m_mutex); - m_handles.push_back(handle); + query->m_next = std::exchange(m_free, query); } - + void DxvkGpuQueryAllocator::createQueryPool() { + auto vk = m_device->vkd(); + VkQueryPoolCreateInfo info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; info.queryType = m_queryType; info.queryCount = m_queryPoolSize; @@ -238,15 +232,26 @@ namespace dxvk { VkQueryPool queryPool = VK_NULL_HANDLE; - if (m_vkd->vkCreateQueryPool(m_vkd->device(), &info, nullptr, &queryPool)) { + if (vk->vkCreateQueryPool(vk->device(), &info, nullptr, &queryPool)) { Logger::err(str::format("DXVK: Failed to create query pool (", m_queryType, "; ", m_queryPoolSize, ")")); return; } - m_pools.push_back(queryPool); + auto& pool = m_pools.emplace_back(); + pool.pool = queryPool; + pool.queries = new DxvkGpuQuery [m_queryPoolSize]; - for (uint32_t i = 0; i < m_queryPoolSize; i++) - m_handles.push_back({ this, queryPool, i }); + for (uint32_t i = 0; i < m_queryPoolSize; i++) { + auto& query = pool.queries[i]; + query.m_allocator = this; + query.m_pool = queryPool; + query.m_index = i; + + if (i + 1u < m_queryPoolSize) + query.m_next = &pool.queries[i + 1u]; + } + + m_free = &pool.queries[0u]; } @@ -266,7 +271,7 @@ namespace dxvk { } - DxvkGpuQueryHandle DxvkGpuQueryPool::allocQuery(VkQueryType type) { + Rc DxvkGpuQueryPool::allocQuery(VkQueryType type) { switch (type) { case VK_QUERY_TYPE_OCCLUSION: return m_occlusion.allocQuery(); @@ -278,7 +283,7 @@ namespace dxvk { return m_xfbStream.allocQuery(); default: Logger::err(str::format("DXVK: Unhandled query type: ", type)); - return DxvkGpuQueryHandle(); + return nullptr; } } @@ -286,7 +291,7 @@ namespace dxvk { DxvkGpuQueryManager::DxvkGpuQueryManager(DxvkGpuQueryPool& pool) - : m_pool(&pool), m_activeTypes(0) { + : m_pool(&pool) { } @@ -299,51 +304,55 @@ namespace dxvk { void DxvkGpuQueryManager::enableQuery( const Rc& cmd, const Rc& query) { - query->begin(cmd); + query->begin(); - m_activeQueries.push_back(query); + uint32_t index = getQueryTypeIndex(query->type(), query->index()); + + m_activeQueries[index].queries.push_back(query); if (m_activeTypes & getQueryTypeBit(query->type())) - beginSingleQuery(cmd, query); + restartQueries(cmd, query->type(), query->index()); } void DxvkGpuQueryManager::disableQuery( const Rc& cmd, const Rc& query) { - auto iter = std::find( - m_activeQueries.begin(), - m_activeQueries.end(), - query); - - if (iter != m_activeQueries.end()) { - if (m_activeTypes & getQueryTypeBit((*iter)->type())) - endSingleQuery(cmd, query); - m_activeQueries.erase(iter); - - query->end(); + uint32_t index = getQueryTypeIndex(query->type(), query->index()); + + for (auto& q : m_activeQueries[index].queries) { + if (q == query) { + q = std::move(m_activeQueries[index].queries.back()); + m_activeQueries[index].queries.pop_back(); + break; + } } + + if (m_activeTypes & getQueryTypeBit(query->type())) + restartQueries(cmd, query->type(), query->index()); + + query->end(); } void DxvkGpuQueryManager::writeTimestamp( const Rc& cmd, const Rc& query) { - DxvkGpuQueryHandle handle = m_pool->allocQuery(query->type()); - - query->begin(cmd); - query->addQueryHandle(handle); + Rc q = m_pool->allocQuery(query->type()); + + query->begin(); + query->addGpuQuery(q); query->end(); - cmd->resetQuery( - handle.queryPool, - handle.queryId); - + std::pair handle = q->getQuery(); + + cmd->resetQuery(handle.first, handle.second); + cmd->cmdWriteTimestamp(DxvkCmdBuffer::ExecBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - handle.queryPool, handle.queryId); - - cmd->trackResource(query); + handle.first, handle.second); + + cmd->trackQuery(std::move(q)); } @@ -352,9 +361,11 @@ namespace dxvk { VkQueryType type) { m_activeTypes |= getQueryTypeBit(type); - for (size_t i = 0; i < m_activeQueries.size(); i++) { - if (m_activeQueries[i]->type() == type) - beginSingleQuery(cmd, m_activeQueries[i]); + if (likely(type != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)) { + restartQueries(cmd, type, 0); + } else { + for (uint32_t i = 0; i < 4; i++) + restartQueries(cmd, type, i); } } @@ -364,88 +375,76 @@ namespace dxvk { VkQueryType type) { m_activeTypes &= ~getQueryTypeBit(type); - for (size_t i = 0; i < m_activeQueries.size(); i++) { - if (m_activeQueries[i]->type() == type) - endSingleQuery(cmd, m_activeQueries[i]); - } - } - - - void DxvkGpuQueryManager::beginSingleQuery( - const Rc& cmd, - const Rc& query) { - DxvkGpuQueryHandle handle = m_pool->allocQuery(query->type()); - - cmd->resetQuery( - handle.queryPool, - handle.queryId); - - if (query->isIndexed()) { - cmd->cmdBeginQueryIndexed( - handle.queryPool, - handle.queryId, - query->flags(), - query->index()); + if (likely(type != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)) { + restartQueries(cmd, type, 0); } else { - cmd->cmdBeginQuery( - handle.queryPool, - handle.queryId, - query->flags()); + for (uint32_t i = 0; i < 4; i++) + restartQueries(cmd, type, i); } - - query->addQueryHandle(handle); } - void DxvkGpuQueryManager::endSingleQuery( + void DxvkGpuQueryManager::restartQueries( const Rc& cmd, - const Rc& query) { - DxvkGpuQueryHandle handle = query->handle(); - - if (query->isIndexed()) { - cmd->cmdEndQueryIndexed( - handle.queryPool, - handle.queryId, - query->index()); - } else { - cmd->cmdEndQuery( - handle.queryPool, - handle.queryId); + VkQueryType type, + uint32_t index) { + auto& array = m_activeQueries[getQueryTypeIndex(type, index)]; + + // End active GPU query for the given type and index + if (array.gpuQuery) { + auto handle = array.gpuQuery->getQuery(); + + if (type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) + cmd->cmdEndQueryIndexed(handle.first, handle.second, index); + else + cmd->cmdEndQuery(handle.first, handle.second); + + array.gpuQuery = nullptr; } - cmd->trackResource(query); + // If the query type is still active, allocate, reset and begin + // a new GPU query and assign it to all virtual queries. + if ((m_activeTypes & getQueryTypeBit(type)) && !array.queries.empty()) { + array.gpuQuery = m_pool->allocQuery(type); + auto handle = array.gpuQuery->getQuery(); + + // If any active occlusion query has the precise flag set, we need + // to respect it, otherwise just use a regular occlusion query. + VkQueryControlFlags flags = 0u; + + for (const auto& q : array.queries) { + flags |= q->flags(); + q->addGpuQuery(array.gpuQuery); + } + + // Actually reset and begin the query + cmd->resetQuery(handle.first, handle.second); + + if (type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) + cmd->cmdBeginQueryIndexed(handle.first, handle.second, flags, index); + else + cmd->cmdBeginQuery(handle.first, handle.second, flags); + + cmd->trackQuery(Rc(array.gpuQuery)); + } } - - + + uint32_t DxvkGpuQueryManager::getQueryTypeBit( VkQueryType type) { + return 1u << getQueryTypeIndex(type, 0u); + } + + + uint32_t DxvkGpuQueryManager::getQueryTypeIndex( + VkQueryType type, + uint32_t index) { switch (type) { - case VK_QUERY_TYPE_OCCLUSION: return 0x01; - case VK_QUERY_TYPE_PIPELINE_STATISTICS: return 0x02; - case VK_QUERY_TYPE_TIMESTAMP: return 0x04; - case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: return 0x08; - default: return 0; + case VK_QUERY_TYPE_OCCLUSION: return 0u; + case VK_QUERY_TYPE_PIPELINE_STATISTICS: return 1u; + case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: return 2u + index; + default: return 0u; } } - - - - DxvkGpuQueryTracker::DxvkGpuQueryTracker() { } - DxvkGpuQueryTracker::~DxvkGpuQueryTracker() { } - - - void DxvkGpuQueryTracker::trackQuery(DxvkGpuQueryHandle handle) { - if (handle.queryPool) - m_handles.push_back(handle); - } - - - void DxvkGpuQueryTracker::reset() { - for (DxvkGpuQueryHandle handle : m_handles) - handle.allocator->freeQuery(handle); - - m_handles.clear(); - } - } \ No newline at end of file diff --git a/src/dxvk/dxvk_gpu_query.h b/src/dxvk/dxvk_gpu_query.h index ad796f719..9f25dbf48 100644 --- a/src/dxvk/dxvk_gpu_query.h +++ b/src/dxvk/dxvk_gpu_query.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "../util/util_small_vector.h" @@ -103,31 +104,83 @@ namespace dxvk { * query pools have to be reset on the GPU, * this also comes with a reset event. */ - struct DxvkGpuQueryHandle { - DxvkGpuQueryAllocator* allocator = nullptr; - VkQueryPool queryPool = VK_NULL_HANDLE; - uint32_t queryId = 0; + class DxvkGpuQuery { + friend class DxvkGpuQueryAllocator; + public: + + /** + * \brief Increments query reference count + */ + force_inline void incRef() { + m_refCount.fetch_add(1u, std::memory_order_acquire); + } + + /** + * \brief Decrements query reference count + * Returns the query to its allocator if necessary. + */ + force_inline void decRef() { + if (m_refCount.fetch_sub(1u, std::memory_order_release) == 1u) + free(); + } + + /** + * \brief Retrieves query pool and index + * \returns Query pool handle and query index + */ + std::pair getQuery() const { + return std::make_pair(m_pool, m_index); + } + + private: + + DxvkGpuQueryAllocator* m_allocator = nullptr; + DxvkGpuQuery* m_next = nullptr; + + VkQueryPool m_pool = VK_NULL_HANDLE; + uint32_t m_index = 0u; + + std::atomic m_refCount = { 0u }; + + void free(); + }; /** - * \brief Query object - * - * Manages Vulkan queries that are sub-allocated - * from larger query pools + * \brief Virtual query object + * + * References an arbitrary number of Vulkan queries to + * get feedback from the GPU. Vulkan queries can be used + * by multiple virtual queries in case of overlap. */ - class DxvkQuery : public DxvkResource { - + class DxvkQuery { + friend class DxvkGpuQueryManager; public: DxvkQuery( - const Rc& vkd, - VkQueryType type, - VkQueryControlFlags flags, - uint32_t index); + const Rc& device, + VkQueryType type, + VkQueryControlFlags flags, + uint32_t index); ~DxvkQuery(); + /** + * \brief Increments reference count + */ + force_inline void incRef() { + m_refCount.fetch_add(1u, std::memory_order_acquire); + } + + /** + * \brief Decrements reference count + */ + force_inline void decRef() { + if (m_refCount.fetch_sub(1u, std::memory_order_release) == 1u) + delete this; + } + /** * \brief Query type * \returns Query type @@ -144,20 +197,6 @@ namespace dxvk { return m_flags; } - /** - * \brief Retrieves current handle - * - * Note that the query handle will change - * when calling \ref addQueryHandle. - * \returns Current query handle - */ - DxvkGpuQueryHandle handle() const { - if (m_handles.empty()) - return DxvkGpuQueryHandle(); - - return m_handles.back(); - } - /** * \brief Query index * @@ -170,12 +209,6 @@ namespace dxvk { return m_index; } - /** - * \brief Checks whether query is indexed - * \returns \c true for indexed query types - */ - bool isIndexed() const; - /** * \brief Retrieves query data * @@ -192,52 +225,42 @@ namespace dxvk { /** * \brief Begins query - * - * Moves all current query handles to the given - * command list and sets the query into active - * state. No data can be retrieved while the - * query is active. - * \param [in] cmd Command list + * + * Invalidates previously retrieved data. */ - void begin( - const Rc& cmd); + void begin(); /** * \brief Ends query - * + * * Sets query into pending state. Calling * \c getData is legal after calling this. */ void end(); - /** - * \brief Adds a query handle to the query - * - * The given query handle shall be used when - * retrieving query data. A query can have - * multiple handles attached. - * \param [in] handle The query handle - */ - void addQueryHandle( - const DxvkGpuQueryHandle& handle); - private: - Rc m_vkd; + std::atomic m_refCount = { 0u }; - VkQueryType m_type; - VkQueryControlFlags m_flags; - uint32_t m_index; - std::atomic m_ended; + Rc m_device; + VkQueryType m_type = VK_QUERY_TYPE_MAX_ENUM; + VkQueryControlFlags m_flags = 0u; + uint32_t m_index = 0u; + bool m_ended = false; + + sync::Spinlock m_mutex; DxvkQueryData m_queryData = { }; - small_vector m_handles; - - DxvkGpuQueryStatus accumulateQueryDataForHandle( - const DxvkGpuQueryHandle& handle); + small_vector, 8> m_queries; - DxvkGpuQueryStatus accumulateQueryData(); + DxvkGpuQueryStatus accumulateQueryDataForGpuQueryLocked( + const Rc& query); + + DxvkGpuQueryStatus accumulateQueryDataLocked(); + + void addGpuQuery( + Rc query); }; @@ -253,42 +276,46 @@ namespace dxvk { public: DxvkGpuQueryAllocator( - DxvkDevice* device, - VkQueryType queryType, - uint32_t queryPoolSize); - + DxvkDevice* device, + VkQueryType queryType, + uint32_t queryPoolSize); + ~DxvkGpuQueryAllocator(); /** * \brief Allocates a query * - * If possible, this returns a free query - * from an existing query pool. Otherwise, - * a new query pool will be created. + * If possible, this returns a free query from an existing + * query pool. Otherwise, a new query pool will be created. * \returns Query handle */ - DxvkGpuQueryHandle allocQuery(); + Rc allocQuery(); /** * \brief Recycles a query * - * Returns a query back to the allocator - * so that it can be reused. The query - * must not be in pending state. - * \param [in] handle Query to reset + * Returns a query back to the allocator so that it can be + * reused. The query must not be in pending state. + * \param [in] query Query object to recycle */ - void freeQuery(DxvkGpuQueryHandle handle); + void freeQuery( + DxvkGpuQuery* query); private: - DxvkDevice* m_device; - Rc m_vkd; - VkQueryType m_queryType; - uint32_t m_queryPoolSize; - - dxvk::mutex m_mutex; - std::vector m_handles; - std::vector m_pools; + struct Pool { + VkQueryPool pool = VK_NULL_HANDLE; + DxvkGpuQuery* queries = nullptr; + }; + + DxvkDevice* m_device = nullptr; + VkQueryType m_queryType = VK_QUERY_TYPE_MAX_ENUM; + uint32_t m_queryPoolSize = 0u; + + dxvk::mutex m_mutex; + std::list m_pools; + + DxvkGpuQuery* m_free = nullptr; void createQueryPool(); @@ -315,7 +342,7 @@ namespace dxvk { * \param [in] type Query type * \returns Handle to the allocated query */ - DxvkGpuQueryHandle allocQuery(VkQueryType type); + Rc allocQuery(VkQueryType type); private: @@ -334,7 +361,7 @@ namespace dxvk { * and assigns Vulkan queries to them as needed. */ class DxvkGpuQueryManager { - + constexpr static uint32_t MaxQueryTypes = 6u; public: DxvkGpuQueryManager(DxvkGpuQueryPool& pool); @@ -402,54 +429,28 @@ namespace dxvk { private: - DxvkGpuQueryPool* m_pool; - uint32_t m_activeTypes; - std::vector> m_activeQueries; + struct QuerySet { + Rc gpuQuery; + std::vector> queries; + }; - void beginSingleQuery( - const Rc& cmd, - const Rc& query); + DxvkGpuQueryPool* m_pool = nullptr; + uint32_t m_activeTypes = 0u; - void endSingleQuery( + std::array m_activeQueries = { }; + + void restartQueries( const Rc& cmd, - const Rc& query); - + VkQueryType type, + uint32_t index); + static uint32_t getQueryTypeBit( VkQueryType type); - }; - - - /** - * \brief Query tracker - * - * Returns queries to their allocators after - * the command buffer has finished executing. - */ - class DxvkGpuQueryTracker { - - public: - - DxvkGpuQueryTracker(); - ~DxvkGpuQueryTracker(); - - /** - * \param Tracks a query - * \param [in] handle Query handle - */ - void trackQuery(DxvkGpuQueryHandle handle); - - /** - * \brief Recycles all tracked handles - * - * Releases all tracked query handles - * to their respective query allocator. - */ - void reset(); - - private: - - std::vector m_handles; + static uint32_t getQueryTypeIndex( + VkQueryType type, + uint32_t index); }; -} \ No newline at end of file + +} diff --git a/src/dxvk/dxvk_lifetime.cpp b/src/dxvk/dxvk_lifetime.cpp index 326b5af5d..1d34ac0a4 100644 --- a/src/dxvk/dxvk_lifetime.cpp +++ b/src/dxvk/dxvk_lifetime.cpp @@ -11,6 +11,7 @@ namespace dxvk { m_allocations.clear(); m_samplers.clear(); m_events.clear(); + m_queries.clear(); } } \ No newline at end of file diff --git a/src/dxvk/dxvk_lifetime.h b/src/dxvk/dxvk_lifetime.h index 501198082..07abee230 100644 --- a/src/dxvk/dxvk_lifetime.h +++ b/src/dxvk/dxvk_lifetime.h @@ -3,6 +3,7 @@ #include #include "dxvk_gpu_event.h" +#include "dxvk_gpu_query.h" #include "dxvk_resource.h" #include "dxvk_sampler.h" @@ -124,6 +125,14 @@ namespace dxvk { m_events.push_back(std::move(event)); } + /** + * \brief Adds a query to track + * \param [in] query The query to track + */ + void trackQuery(Rc&& query) { + m_queries.push_back(std::move(query)); + } + /** * \brief Adds a resource to track * \param [in] res The resource to track @@ -152,6 +161,7 @@ namespace dxvk { std::vector> m_samplers; std::vector> m_events; + std::vector> m_queries; std::vector> m_resources; std::vector> m_allocations;