diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 505eb476..856f0f87 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -124,6 +124,7 @@ namespace dxvk { void DxvkCommandList::reset() { m_statCounters.reset(); m_bufferTracker.reset(); + m_gpuQueryTracker.reset(); m_gpuEventTracker.reset(); m_eventTracker.reset(); m_queryTracker.reset(); diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index 6b662bc4..d6a0012a 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -7,6 +7,7 @@ #include "dxvk_descriptor.h" #include "dxvk_event.h" #include "dxvk_gpu_event.h" +#include "dxvk_gpu_query.h" #include "dxvk_lifetime.h" #include "dxvk_limits.h" #include "dxvk_pipelayout.h" @@ -176,6 +177,17 @@ namespace dxvk { m_gpuEventTracker.trackEvent(handle); } + /** + * \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); + } + /** * \brief Signals tracked events * @@ -552,6 +564,23 @@ namespace dxvk { m_vkd->vkCmdPushConstants(m_execBuffer, layout, stageFlags, offset, size, pValues); } + + + void cmdResetQuery( + VkQueryPool queryPool, + uint32_t queryId, + VkEvent event) { + m_cmdBuffersUsed.set(DxvkCmdBufferFlag::InitBuffer); + + m_vkd->vkResetEvent( + m_vkd->device(), event); + + m_vkd->vkCmdResetQueryPool( + m_initBuffer, queryPool, queryId, 1); + + m_vkd->vkCmdSetEvent(m_initBuffer, + event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + } void cmdResetQueryPool( @@ -681,6 +710,7 @@ namespace dxvk { DxvkQueryTracker m_queryTracker; DxvkEventTracker m_eventTracker; DxvkGpuEventTracker m_gpuEventTracker; + DxvkGpuQueryTracker m_gpuQueryTracker; DxvkBufferTracker m_bufferTracker; DxvkStatCounters m_statCounters; diff --git a/src/dxvk/dxvk_gpu_query.cpp b/src/dxvk/dxvk_gpu_query.cpp new file mode 100644 index 00000000..17d9c678 --- /dev/null +++ b/src/dxvk/dxvk_gpu_query.cpp @@ -0,0 +1,451 @@ +#include + +#include "dxvk_cmdlist.h" +#include "dxvk_gpu_query.h" + +namespace dxvk { + + DxvkGpuQuery::DxvkGpuQuery( + 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) { + + } + + + DxvkGpuQuery::~DxvkGpuQuery() { + if (m_handle.queryPool) + m_handle.allocator->freeQuery(m_handle); + + for (DxvkGpuQueryHandle handle : m_handles) + handle.allocator->freeQuery(handle); + } + + + bool DxvkGpuQuery::isIndexed() const { + return m_type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT; + } + + + DxvkGpuQueryStatus DxvkGpuQuery::getData(DxvkQueryData& queryData) const { + queryData = DxvkQueryData(); + + if (!m_ended) + return DxvkGpuQueryStatus::Invalid; + + // Empty begin/end pair + if (!m_handle.queryPool) + return DxvkGpuQueryStatus::Available; + + // Get query data from all associated handles + DxvkGpuQueryStatus status = getDataForHandle(queryData, m_handle); + + for (size_t i = 0; i < m_handles.size() + && status == DxvkGpuQueryStatus::Available; i++) + status = getDataForHandle(queryData, m_handles[i]); + + // Treat non-precise occlusion queries as available + // if we already know the result will be non-zero + if ((status == DxvkGpuQueryStatus::Pending) + && (m_type == VK_QUERY_TYPE_OCCLUSION) + && !(m_flags & VK_QUERY_CONTROL_PRECISE_BIT) + && (queryData.occlusion.samplesPassed)) + status = DxvkGpuQueryStatus::Available; + + return status; + } + + + void DxvkGpuQuery::begin(const Rc& cmd) { + m_ended = false; + + cmd->trackGpuQuery(m_handle); + m_handle = DxvkGpuQueryHandle(); + + for (const auto& handle : m_handles) + cmd->trackGpuQuery(handle); + m_handles.clear(); + } + + + void DxvkGpuQuery::end() { + m_ended = true; + } + + + void DxvkGpuQuery::addQueryHandle(const DxvkGpuQueryHandle& handle) { + if (m_handle.queryPool) + m_handles.push_back(m_handle); + + m_handle = handle; + } + + + DxvkGpuQueryStatus DxvkGpuQuery::getDataForHandle( + DxvkQueryData& queryData, + const DxvkGpuQueryHandle& handle) const { + DxvkQueryData tmpData; + + // Wait for the query to be reset first + VkResult result = m_vkd->vkGetEventStatus( + m_vkd->device(), handle.resetEvent); + + if (result == VK_EVENT_RESET) + return DxvkGpuQueryStatus::Pending; + else if (result != VK_EVENT_SET) + return DxvkGpuQueryStatus::Failed; + + // Try to copy query data to temporary structure + result = m_vkd->vkGetQueryPoolResults(m_vkd->device(), + handle.queryPool, handle.queryId, 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: + queryData.occlusion.samplesPassed += tmpData.occlusion.samplesPassed; + break; + + case VK_QUERY_TYPE_TIMESTAMP: + queryData.timestamp.time = tmpData.timestamp.time; + break; + + case VK_QUERY_TYPE_PIPELINE_STATISTICS: + queryData.statistic.iaVertices += tmpData.statistic.iaVertices; + queryData.statistic.iaPrimitives += tmpData.statistic.iaPrimitives; + queryData.statistic.vsInvocations += tmpData.statistic.vsInvocations; + queryData.statistic.gsInvocations += tmpData.statistic.gsInvocations; + queryData.statistic.gsPrimitives += tmpData.statistic.gsPrimitives; + queryData.statistic.clipInvocations += tmpData.statistic.clipInvocations; + queryData.statistic.clipPrimitives += tmpData.statistic.clipPrimitives; + queryData.statistic.fsInvocations += tmpData.statistic.fsInvocations; + queryData.statistic.tcsPatches += tmpData.statistic.tcsPatches; + queryData.statistic.tesInvocations += tmpData.statistic.tesInvocations; + queryData.statistic.csInvocations += tmpData.statistic.csInvocations; + break; + + case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: + queryData.xfbStream.primitivesWritten += tmpData.xfbStream.primitivesWritten; + queryData.xfbStream.primitivesNeeded += tmpData.xfbStream.primitivesNeeded; + break; + + default: + Logger::err(str::format("DXVK: Unhandled query type: ", m_type)); + return DxvkGpuQueryStatus::Invalid; + } + + return DxvkGpuQueryStatus::Available; + } + + + + + DxvkGpuQueryAllocator::DxvkGpuQueryAllocator( + const Rc& vkd, + VkQueryType queryType, + uint32_t queryPoolSize) + : m_vkd (vkd), + m_queryType (queryType), + m_queryPoolSize (queryPoolSize) { + + } + + + DxvkGpuQueryAllocator::~DxvkGpuQueryAllocator() { + for (DxvkGpuQueryHandle handle : m_handles) { + m_vkd->vkDestroyEvent(m_vkd->device(), + handle.resetEvent, nullptr); + } + + for (VkQueryPool pool : m_pools) { + m_vkd->vkDestroyQueryPool( + m_vkd->device(), pool, nullptr); + } + } + + + DxvkGpuQueryHandle DxvkGpuQueryAllocator::allocQuery() { + std::lock_guard lock(m_mutex); + + if (m_handles.size() == 0) + this->createQueryPool(); + + if (m_handles.size() == 0) + return DxvkGpuQueryHandle(); + + DxvkGpuQueryHandle result = m_handles.back(); + m_handles.pop_back(); + return result; + } + + + void DxvkGpuQueryAllocator::freeQuery(DxvkGpuQueryHandle handle) { + std::lock_guard lock(m_mutex); + m_handles.push_back(handle); + } + + + void DxvkGpuQueryAllocator::createQueryPool() { + VkQueryPoolCreateInfo info; + info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + info.queryType = m_queryType; + info.queryCount = m_queryPoolSize; + info.pipelineStatistics = 0; + + if (m_queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) { + info.pipelineStatistics + = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT + | VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT + | VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT + | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT + | VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT + | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT + | VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT; + } + + VkQueryPool queryPool = VK_NULL_HANDLE; + + if (m_vkd->vkCreateQueryPool(m_vkd->device(), &info, nullptr, &queryPool)) { + Logger::err(str::format("DXVK: Failed to create query pool (", m_queryType, "; ", m_queryPoolSize, ")")); + return; + } + + m_pools.push_back(queryPool); + + VkEventCreateInfo eventInfo; + eventInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO; + eventInfo.pNext = nullptr; + eventInfo.flags = 0; + + for (uint32_t i = 0; i < m_queryPoolSize; i++) { + VkEvent event = VK_NULL_HANDLE; + + if (m_vkd->vkCreateEvent(m_vkd->device(), &eventInfo, nullptr, &event)) { + Logger::err("DXVK: Failed to create query reset event"); + return; + } + + m_handles.push_back({ this, event, queryPool, i }); + } + } + + + + + DxvkGpuQueryPool::DxvkGpuQueryPool(const Rc& vkd) + : m_occlusion(vkd, VK_QUERY_TYPE_OCCLUSION, 256), + m_statistic(vkd, VK_QUERY_TYPE_PIPELINE_STATISTICS, 64), + m_timestamp(vkd, VK_QUERY_TYPE_TIMESTAMP, 64), + m_xfbStream(vkd, VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 64) { + + } + + + DxvkGpuQueryPool::~DxvkGpuQueryPool() { + + } + + + DxvkGpuQueryHandle DxvkGpuQueryPool::allocQuery(VkQueryType type) { + switch (type) { + case VK_QUERY_TYPE_OCCLUSION: + return m_occlusion.allocQuery(); + case VK_QUERY_TYPE_PIPELINE_STATISTICS: + return m_statistic.allocQuery(); + case VK_QUERY_TYPE_TIMESTAMP: + return m_timestamp.allocQuery(); + case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: + return m_xfbStream.allocQuery(); + default: + Logger::err(str::format("DXVK: Unhandled query type: ", type)); + return DxvkGpuQueryHandle(); + } + } + + + + + DxvkGpuQueryManager::DxvkGpuQueryManager( + const Rc& pool) + : m_pool(pool), m_activeTypes(0) { + + } + + + DxvkGpuQueryManager::~DxvkGpuQueryManager() { + + } + + + void DxvkGpuQueryManager::enableQuery( + const Rc& cmd, + const Rc& query) { + query->begin(cmd); + + m_activeQueries.push_back(query); + + if (m_activeTypes & getQueryTypeBit(query->type())) + beginSingleQuery(cmd, query); + } + + + 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()) { + m_activeQueries.erase(iter); + + if (m_activeTypes & getQueryTypeBit((*iter)->type())) + endSingleQuery(cmd, query); + + query->end(); + } + } + + + void DxvkGpuQueryManager::writeTimestamp( + const Rc& cmd, + const Rc& query) { + DxvkGpuQueryHandle handle = m_pool->allocQuery(query->type()); + + query->begin(cmd); + query->addQueryHandle(handle); + query->end(); + + cmd->cmdResetQuery( + handle.queryPool, + handle.queryId, + handle.resetEvent); + + cmd->cmdWriteTimestamp( + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + handle.queryPool, + handle.queryId); + + cmd->trackResource(query); + } + + + void DxvkGpuQueryManager::beginQueries( + const Rc& cmd, + 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]); + } + } + + + void DxvkGpuQueryManager::endQueries( + const Rc& cmd, + 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->cmdResetQuery( + handle.queryPool, + handle.queryId, + handle.resetEvent); + + if (query->isIndexed()) { + cmd->cmdBeginQueryIndexed( + handle.queryPool, + handle.queryId, + query->flags(), + query->index()); + } else { + cmd->cmdBeginQuery( + handle.queryPool, + handle.queryId, + query->flags()); + } + + query->addQueryHandle(handle); + } + + + void DxvkGpuQueryManager::endSingleQuery( + 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); + } + + cmd->trackResource(query); + } + + + uint32_t DxvkGpuQueryManager::getQueryTypeBit( + VkQueryType type) { + 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; + } + } + + + + + 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 new file mode 100644 index 00000000..0f900e0b --- /dev/null +++ b/src/dxvk/dxvk_gpu_query.h @@ -0,0 +1,385 @@ +#pragma once + +#include +#include + +#include "dxvk_query.h" +#include "dxvk_resource.h" + +namespace dxvk { + + class DxvkCommandList; + + class DxvkGpuQueryPool; + class DxvkGpuQueryAllocator; + + /** + * \brief Query status + * + * Reports whether a query is in + * signaled or unsignaled state. + */ + enum class DxvkGpuQueryStatus : uint32_t { + Invalid = 0, + Pending = 1, + Available = 2, + Failed = 3, + }; + + + /** + * \brief Query handle + * + * Stores the query allocator, as well as + * the actual pool and query index. Since + * query pools have to be reset on the GPU, + * this also comes with a reset event. + */ + struct DxvkGpuQueryHandle { + DxvkGpuQueryAllocator* allocator = nullptr; + VkEvent resetEvent = VK_NULL_HANDLE; + VkQueryPool queryPool = VK_NULL_HANDLE; + uint32_t queryId = 0; + }; + + + /** + * \brief Query object + * + * Manages Vulkan queries that are sub-allocated + * from larger query pools + */ + class DxvkGpuQuery : public DxvkResource { + + public: + + DxvkGpuQuery( + const Rc& vkd, + VkQueryType type, + VkQueryControlFlags flags, + uint32_t index); + + ~DxvkGpuQuery(); + + /** + * \brief Query type + * \returns Query type + */ + VkQueryType type() const { + return m_type; + } + + /** + * \brief Query control flags + * \returns Query control flags + */ + VkQueryControlFlags flags() const { + 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 { + return m_handle; + } + + /** + * \brief Query index + * + * Only valid for indexed query types. + * For non-zero values, indexed query + * functions must be used. + * \returns Query index + */ + uint32_t index() const { + return m_index; + } + + /** + * \brief Checks whether query is indexed + * \returns \c true for indexed query types + */ + bool isIndexed() const; + + /** + * \brief Retrieves query data + * + * If all query data is available, this will + * return \c DxvkGpuQueryStatus::Signaled, and + * the destination structure will be filled + * with the data retrieved from all associated + * query handles. + * \param [out] queryData Query data + * \returns Current query status + */ + DxvkGpuQueryStatus getData( + DxvkQueryData& queryData) const; + + /** + * \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 + */ + void begin( + const Rc& cmd); + + /** + * \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; + + VkQueryType m_type; + VkQueryControlFlags m_flags; + uint32_t m_index; + bool m_ended; + + DxvkGpuQueryHandle m_handle; + + std::vector m_handles; + + DxvkGpuQueryStatus getDataForHandle( + DxvkQueryData& queryData, + const DxvkGpuQueryHandle& handle) const; + + }; + + + /** + * \brief Query allocator + * + * Creates query pools and allocates + * queries for a single query type. + */ + class DxvkGpuQueryAllocator { + + public: + + DxvkGpuQueryAllocator( + const Rc& vkd, + 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. + * \returns Query handle + */ + DxvkGpuQueryHandle 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 + */ + void freeQuery(DxvkGpuQueryHandle handle); + + private: + + Rc m_vkd; + VkQueryType m_queryType; + uint32_t m_queryPoolSize; + + std::mutex m_mutex; + std::vector m_handles; + std::vector m_pools; + + void createQueryPool(); + + }; + + + /** + * \brief Query pool + * + * Small wrapper class that manages query + * allocators for all supported query types, + */ + class DxvkGpuQueryPool : public RcObject { + + public: + + DxvkGpuQueryPool( + const Rc& vkd); + + ~DxvkGpuQueryPool(); + + /** + * \brief Allocates a single query + * + * \param [in] type Query type + * \returns Handle to the allocated query + */ + DxvkGpuQueryHandle allocQuery(VkQueryType type); + + private: + + DxvkGpuQueryAllocator m_occlusion; + DxvkGpuQueryAllocator m_statistic; + DxvkGpuQueryAllocator m_timestamp; + DxvkGpuQueryAllocator m_xfbStream; + + }; + + + /** + * \brief Query manager + * + * Keeps track of enabled and disabled queries + * and assigns Vulkan queries to them as needed. + */ + class DxvkGpuQueryManager { + + public: + + DxvkGpuQueryManager( + const Rc& pool); + + ~DxvkGpuQueryManager(); + + /** + * \brief Enables a query + * + * This will also immediately begin the + * query in case the query type is active. + * \param [in] cmd Command list + * \param [in] query Query to allocate + */ + void enableQuery( + const Rc& cmd, + const Rc& query); + + /** + * \brief Disables a query + * + * This will also immediately end the + * query in case the query type is active. + * \param [in] cmd Command list + * \param [in] query Query to allocate + */ + void disableQuery( + const Rc& cmd, + const Rc& query); + + /** + * \brief Signals a time stamp query + * + * Timestamp queries are not scoped. + * \param [in] cmd Command list + * \param [in] query Query to allocate + */ + void writeTimestamp( + const Rc& cmd, + const Rc& query); + + /** + * \brief Begins queries of a given type + * + * Makes a query type \e active. Begins + * all enabled queries of this type. + * \param [in] cmd Command list + * \param [in] type Query type + */ + void beginQueries( + const Rc& cmd, + VkQueryType type); + + /** + * \brief Ends queries of a given type + * + * Makes a query type \e inactive. Ends + * all enabled queries of this type. + * \param [in] cmd Command list + * \param [in] type Query type + */ + void endQueries( + const Rc& cmd, + VkQueryType type); + + private: + + Rc m_pool; + uint32_t m_activeTypes; + std::vector> m_activeQueries; + + void beginSingleQuery( + const Rc& cmd, + const Rc& query); + + void endSingleQuery( + const Rc& cmd, + const Rc& query); + + 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; + + }; +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index b50901e5..d91f6873 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -56,6 +56,7 @@ dxvk_src = files([ 'dxvk_format.cpp', 'dxvk_framebuffer.cpp', 'dxvk_gpu_event.cpp', + 'dxvk_gpu_query.cpp', 'dxvk_graphics.cpp', 'dxvk_image.cpp', 'dxvk_instance.cpp',