mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
[dxvk] Add new query implementation
This commit is contained in:
parent
8c3900c533
commit
772fa3074f
@ -124,6 +124,7 @@ namespace dxvk {
|
|||||||
void DxvkCommandList::reset() {
|
void DxvkCommandList::reset() {
|
||||||
m_statCounters.reset();
|
m_statCounters.reset();
|
||||||
m_bufferTracker.reset();
|
m_bufferTracker.reset();
|
||||||
|
m_gpuQueryTracker.reset();
|
||||||
m_gpuEventTracker.reset();
|
m_gpuEventTracker.reset();
|
||||||
m_eventTracker.reset();
|
m_eventTracker.reset();
|
||||||
m_queryTracker.reset();
|
m_queryTracker.reset();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "dxvk_descriptor.h"
|
#include "dxvk_descriptor.h"
|
||||||
#include "dxvk_event.h"
|
#include "dxvk_event.h"
|
||||||
#include "dxvk_gpu_event.h"
|
#include "dxvk_gpu_event.h"
|
||||||
|
#include "dxvk_gpu_query.h"
|
||||||
#include "dxvk_lifetime.h"
|
#include "dxvk_lifetime.h"
|
||||||
#include "dxvk_limits.h"
|
#include "dxvk_limits.h"
|
||||||
#include "dxvk_pipelayout.h"
|
#include "dxvk_pipelayout.h"
|
||||||
@ -176,6 +177,17 @@ namespace dxvk {
|
|||||||
m_gpuEventTracker.trackEvent(handle);
|
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
|
* \brief Signals tracked events
|
||||||
*
|
*
|
||||||
@ -552,6 +564,23 @@ namespace dxvk {
|
|||||||
m_vkd->vkCmdPushConstants(m_execBuffer,
|
m_vkd->vkCmdPushConstants(m_execBuffer,
|
||||||
layout, stageFlags, offset, size, pValues);
|
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(
|
void cmdResetQueryPool(
|
||||||
@ -681,6 +710,7 @@ namespace dxvk {
|
|||||||
DxvkQueryTracker m_queryTracker;
|
DxvkQueryTracker m_queryTracker;
|
||||||
DxvkEventTracker m_eventTracker;
|
DxvkEventTracker m_eventTracker;
|
||||||
DxvkGpuEventTracker m_gpuEventTracker;
|
DxvkGpuEventTracker m_gpuEventTracker;
|
||||||
|
DxvkGpuQueryTracker m_gpuQueryTracker;
|
||||||
DxvkBufferTracker m_bufferTracker;
|
DxvkBufferTracker m_bufferTracker;
|
||||||
DxvkStatCounters m_statCounters;
|
DxvkStatCounters m_statCounters;
|
||||||
|
|
||||||
|
451
src/dxvk/dxvk_gpu_query.cpp
Normal file
451
src/dxvk/dxvk_gpu_query.cpp
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "dxvk_cmdlist.h"
|
||||||
|
#include "dxvk_gpu_query.h"
|
||||||
|
|
||||||
|
namespace dxvk {
|
||||||
|
|
||||||
|
DxvkGpuQuery::DxvkGpuQuery(
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<DxvkCommandList>& 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<vk::DeviceFn>& 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<std::mutex> 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<std::mutex> 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<vk::DeviceFn>& 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<DxvkGpuQueryPool>& pool)
|
||||||
|
: m_pool(pool), m_activeTypes(0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DxvkGpuQueryManager::~DxvkGpuQueryManager() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkGpuQueryManager::enableQuery(
|
||||||
|
const Rc<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& query) {
|
||||||
|
query->begin(cmd);
|
||||||
|
|
||||||
|
m_activeQueries.push_back(query);
|
||||||
|
|
||||||
|
if (m_activeTypes & getQueryTypeBit(query->type()))
|
||||||
|
beginSingleQuery(cmd, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DxvkGpuQueryManager::disableQuery(
|
||||||
|
const Rc<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkCommandList>& 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<DxvkCommandList>& 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<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
385
src/dxvk/dxvk_gpu_query.h
Normal file
385
src/dxvk/dxvk_gpu_query.h
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<vk::DeviceFn>& 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<DxvkCommandList>& 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<vk::DeviceFn> m_vkd;
|
||||||
|
|
||||||
|
VkQueryType m_type;
|
||||||
|
VkQueryControlFlags m_flags;
|
||||||
|
uint32_t m_index;
|
||||||
|
bool m_ended;
|
||||||
|
|
||||||
|
DxvkGpuQueryHandle m_handle;
|
||||||
|
|
||||||
|
std::vector<DxvkGpuQueryHandle> 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<vk::DeviceFn>& 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<vk::DeviceFn> m_vkd;
|
||||||
|
VkQueryType m_queryType;
|
||||||
|
uint32_t m_queryPoolSize;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::vector<DxvkGpuQueryHandle> m_handles;
|
||||||
|
std::vector<VkQueryPool> 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<vk::DeviceFn>& 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<DxvkGpuQueryPool>& 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<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkCommandList>& 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<DxvkCommandList>& cmd,
|
||||||
|
VkQueryType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Rc<DxvkGpuQueryPool> m_pool;
|
||||||
|
uint32_t m_activeTypes;
|
||||||
|
std::vector<Rc<DxvkGpuQuery>> m_activeQueries;
|
||||||
|
|
||||||
|
void beginSingleQuery(
|
||||||
|
const Rc<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& query);
|
||||||
|
|
||||||
|
void endSingleQuery(
|
||||||
|
const Rc<DxvkCommandList>& cmd,
|
||||||
|
const Rc<DxvkGpuQuery>& 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<DxvkGpuQueryHandle> m_handles;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
@ -56,6 +56,7 @@ dxvk_src = files([
|
|||||||
'dxvk_format.cpp',
|
'dxvk_format.cpp',
|
||||||
'dxvk_framebuffer.cpp',
|
'dxvk_framebuffer.cpp',
|
||||||
'dxvk_gpu_event.cpp',
|
'dxvk_gpu_event.cpp',
|
||||||
|
'dxvk_gpu_query.cpp',
|
||||||
'dxvk_graphics.cpp',
|
'dxvk_graphics.cpp',
|
||||||
'dxvk_image.cpp',
|
'dxvk_image.cpp',
|
||||||
'dxvk_instance.cpp',
|
'dxvk_instance.cpp',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user