mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-19 05:52:11 +01:00
[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.
This commit is contained in:
parent
1b9c59c964
commit
693ee7898d
@ -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();
|
||||
|
@ -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<DxvkGpuQuery>&& 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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#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<vk::DeviceFn>& 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<DxvkDevice>& 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<DxvkCommandList>& 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<DxvkGpuQuery> 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<DxvkGpuQuery>& 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<VkQueryPool, uint32_t> 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<DxvkGpuQuery> DxvkGpuQueryAllocator::allocQuery() {
|
||||
std::lock_guard<dxvk::mutex> 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<dxvk::mutex> 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<DxvkGpuQuery> 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<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& 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<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& 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<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& query) {
|
||||
DxvkGpuQueryHandle handle = m_pool->allocQuery(query->type());
|
||||
|
||||
query->begin(cmd);
|
||||
query->addQueryHandle(handle);
|
||||
Rc<DxvkGpuQuery> q = m_pool->allocQuery(query->type());
|
||||
|
||||
query->begin();
|
||||
query->addGpuQuery(q);
|
||||
query->end();
|
||||
|
||||
cmd->resetQuery(
|
||||
handle.queryPool,
|
||||
handle.queryId);
|
||||
|
||||
std::pair<VkQueryPool, uint32_t> 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<DxvkAccess::None>(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<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& 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<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& 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<DxvkAccess::None>(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<DxvkGpuQuery>(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();
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#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<VkQueryPool, uint32_t> 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<uint32_t> 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<vk::DeviceFn>& vkd,
|
||||
VkQueryType type,
|
||||
VkQueryControlFlags flags,
|
||||
uint32_t index);
|
||||
const Rc<DxvkDevice>& 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<DxvkCommandList>& 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<vk::DeviceFn> m_vkd;
|
||||
std::atomic<uint32_t> m_refCount = { 0u };
|
||||
|
||||
VkQueryType m_type;
|
||||
VkQueryControlFlags m_flags;
|
||||
uint32_t m_index;
|
||||
std::atomic<bool> m_ended;
|
||||
Rc<DxvkDevice> 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<DxvkGpuQueryHandle, 8> m_handles;
|
||||
|
||||
DxvkGpuQueryStatus accumulateQueryDataForHandle(
|
||||
const DxvkGpuQueryHandle& handle);
|
||||
small_vector<Rc<DxvkGpuQuery>, 8> m_queries;
|
||||
|
||||
DxvkGpuQueryStatus accumulateQueryData();
|
||||
DxvkGpuQueryStatus accumulateQueryDataForGpuQueryLocked(
|
||||
const Rc<DxvkGpuQuery>& query);
|
||||
|
||||
DxvkGpuQueryStatus accumulateQueryDataLocked();
|
||||
|
||||
void addGpuQuery(
|
||||
Rc<DxvkGpuQuery> 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<DxvkGpuQuery> 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<vk::DeviceFn> m_vkd;
|
||||
VkQueryType m_queryType;
|
||||
uint32_t m_queryPoolSize;
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
std::vector<DxvkGpuQueryHandle> m_handles;
|
||||
std::vector<VkQueryPool> 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<Pool> 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<DxvkGpuQuery> 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<Rc<DxvkQuery>> m_activeQueries;
|
||||
struct QuerySet {
|
||||
Rc<DxvkGpuQuery> gpuQuery;
|
||||
std::vector<Rc<DxvkQuery>> queries;
|
||||
};
|
||||
|
||||
void beginSingleQuery(
|
||||
const Rc<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& query);
|
||||
DxvkGpuQueryPool* m_pool = nullptr;
|
||||
uint32_t m_activeTypes = 0u;
|
||||
|
||||
void endSingleQuery(
|
||||
std::array<QuerySet, MaxQueryTypes> m_activeQueries = { };
|
||||
|
||||
void restartQueries(
|
||||
const Rc<DxvkCommandList>& cmd,
|
||||
const Rc<DxvkQuery>& 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<DxvkGpuQueryHandle> m_handles;
|
||||
static uint32_t getQueryTypeIndex(
|
||||
VkQueryType type,
|
||||
uint32_t index);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ namespace dxvk {
|
||||
m_allocations.clear();
|
||||
m_samplers.clear();
|
||||
m_events.clear();
|
||||
m_queries.clear();
|
||||
}
|
||||
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#include <vector>
|
||||
|
||||
#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<DxvkGpuQuery>&& 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<Rc<DxvkSampler>> m_samplers;
|
||||
std::vector<Rc<DxvkGpuEvent>> m_events;
|
||||
std::vector<Rc<DxvkGpuQuery>> m_queries;
|
||||
|
||||
std::vector<DxvkLifetime<DxvkResource>> m_resources;
|
||||
std::vector<DxvkLifetime<DxvkResourceAllocation>> m_allocations;
|
||||
|
Loading…
x
Reference in New Issue
Block a user