-
Notifications
You must be signed in to change notification settings - Fork 41
[query-pool] Add wrapper for Vulkan query pools #393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
43f0d1c
[query-pool] Add wrapper for Vulkan query pools
IAmNotHanni 11ba06f
[cmake] Cleanup CMake
IAmNotHanni 5c87dcd
[query-pools] Fix clang-tidy warnings
IAmNotHanni 12bfcd0
[query-pool] Fix clang-tidy warnings
IAmNotHanni c909cd1
[query-pool] Fix clang-tidy warnings
IAmNotHanni a9c5c7b
[query-pool] Fix build
IAmNotHanni e1c1b1b
[make-info] Sort templated methods alphabetically
IAmNotHanni 90ca85b
[query-pool] Fix constructor
IAmNotHanni 021e685
[query-pool] Avoid unnecessary static_cast
IAmNotHanni fbc66fd
[query-pool] Take const reference to CommandPool wrapper as argument
IAmNotHanni b0f4386
[query-pool] Avoid unnecessary static_casts
IAmNotHanni bf8ceb6
[query-pool] Fix code formatting
IAmNotHanni 68588d8
[query-pool] Make validate_pipeline_stats_flag_bits more clear
IAmNotHanni e02dd5d
[query-pool] Fix query pool creation bug which sets query count to 0
IAmNotHanni 761e972
[query-pool] Remove unnecessary comment
IAmNotHanni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#pragma once | ||
|
||
#include <vulkan/vulkan_core.h> | ||
|
||
#include <string> | ||
#include <vector> | ||
|
||
namespace inexor::vulkan_renderer::wrapper { | ||
|
||
// Forward declaration of device wrapper | ||
class Device; | ||
class CommandBuffer; | ||
|
||
/// @brief A wrapper for Vulkan query pools | ||
class QueryPool { | ||
private: | ||
const Device &m_device; | ||
VkPhysicalDeviceFeatures m_device_features{}; | ||
VkQueryPool m_query_pool{}; | ||
std::vector<std::uint64_t> m_pipeline_stats; | ||
std::vector<std::string> m_pipeline_stat_names; | ||
|
||
/// @brief These pipeline statistics are enabled by default if the default constructor is used. | ||
/// @note We are not storing these as VkQueryPipelineStatisticFlags, because we need to perform additional checks | ||
/// for some of these flags in order to use them. For example we need to check if tessellation is enabled in order | ||
/// to query its performance. Please note computer shaders do not require special checks. | ||
/// <a href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#queries-pipestats"> | ||
/// Vulkan specification: Pipeline Statistics Queries</a> | ||
const std::vector<VkQueryPipelineStatisticFlagBits> default_pipeline_stats_flag_bits = { | ||
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_CLIPPING_INVOCATIONS_BIT, | ||
VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT, | ||
VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT, | ||
VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT, // requires geometry shaders | ||
VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT, // requires geometry shaders | ||
VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT, // requires tesselation shaders | ||
VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT, // requires tesselation shaders | ||
VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT}; | ||
|
||
[[nodiscard]] static std::string get_pipeline_stats_flag_bit_name(VkQueryPipelineStatisticFlagBits bit); | ||
|
||
/// @brief Validates every specified VkQueryPipelineStatisticFlagBits into one VkQueryPipelineStatisticFlags. | ||
/// Some VkQueryPipelineStatisticFlagBits values require special checks (tesselation shaders for example). | ||
/// @return A vector which contains the valid Vulkan pipeline query statistics flag bits. | ||
[[nodiscard]] std::vector<VkQueryPipelineStatisticFlagBits> | ||
validate_pipeline_stats_flag_bits(const std::vector<VkQueryPipelineStatisticFlagBits> &pipeline_stats_flag_bits); | ||
|
||
public: | ||
/// @brief Construct a Vulkan Query Pool using the default pipeline statistics. | ||
/// @param device The const reference to a device RAII wrapper instance. | ||
/// @param name The internal name. | ||
QueryPool(const Device &device, const std::string &name); | ||
|
||
/// Call <a href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCreateQueryPool"> | ||
/// vkCreateQueryPool</a> | ||
/// @param device The device wrapper. | ||
/// @param name The internal name of this performance query. | ||
/// @param pipeline_stats_flag_bits The enabled <a | ||
/// href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VkQueryPipelineStatisticFlagBits"> | ||
/// Vulkan pipeline statistics flags</a>. | ||
QueryPool(const Device &device, const std::string &name, | ||
const std::vector<VkQueryPipelineStatisticFlagBits> &pipeline_stats_flag_bits); | ||
|
||
QueryPool(const QueryPool &) = delete; | ||
QueryPool(QueryPool &&) = delete; | ||
~QueryPool(); | ||
|
||
QueryPool operator=(const QueryPool &) = delete; | ||
QueryPool operator=(QueryPool &&) = delete; | ||
|
||
[[nodiscard]] const wrapper::Device &device() const noexcept { | ||
return m_device; | ||
} | ||
|
||
/// Call <a | ||
/// href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCmdResetQueryPool"> | ||
/// vkCmdResetQueryPool</a> | ||
void reset(const wrapper::CommandBuffer &cmd_buffer) const; | ||
|
||
/// Call <a href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCmdBeginQuery"> | ||
/// vkCmdBeginQuery</a> | ||
void begin(const wrapper::CommandBuffer &cmd_buffer) const; | ||
|
||
/// Call <a href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCmdEndQuery"> | ||
/// vkCmdEndQuery</a> | ||
void end(const wrapper::CommandBuffer &cmd_buffer) const; | ||
|
||
/// Call <a | ||
/// href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetQueryPoolResults"> | ||
/// vkGetQueryPoolResults</a> | ||
void get_results(); | ||
|
||
/// TODO: Implement a get method for the results which returns a tuple of query name and result? | ||
|
||
/// Print all the captured pipeline statistics. | ||
void print_results() const; | ||
}; | ||
|
||
} // namespace inexor::vulkan_renderer::wrapper |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
#include "inexor/vulkan-renderer/wrapper/query_pool.hpp" | ||
|
||
#include "inexor/vulkan-renderer/exception.hpp" | ||
#include "inexor/vulkan-renderer/wrapper/command_buffer.hpp" | ||
#include "inexor/vulkan-renderer/wrapper/device.hpp" | ||
#include "inexor/vulkan-renderer/wrapper/make_info.hpp" | ||
|
||
#include <spdlog/spdlog.h> | ||
|
||
namespace inexor::vulkan_renderer::wrapper { | ||
|
||
std::vector<VkQueryPipelineStatisticFlagBits> QueryPool::validate_pipeline_stats_flag_bits( | ||
IAmNotHanni marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const std::vector<VkQueryPipelineStatisticFlagBits> &pipeline_stats_flag_bits) { | ||
std::vector<VkQueryPipelineStatisticFlagBits> ret_val; | ||
|
||
ret_val.reserve(default_pipeline_stats_flag_bits.size()); | ||
|
||
for (const auto flag_bit : pipeline_stats_flag_bits) { | ||
switch (flag_bit) { | ||
case VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT: | ||
if (m_device_features.tessellationShader == VK_FALSE) { | ||
spdlog::warn("Can't add VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT to " | ||
"pipeline statistics flag bit!"); | ||
spdlog::warn( | ||
"Tesselation shaders are not available on this gpu (device_features.tessellationShader = false)"); | ||
break; | ||
} else { | ||
// Tesselation shaders are available so it's safe to add this flag. | ||
ret_val.emplace_back(VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT); | ||
} | ||
break; | ||
case VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT: | ||
if (m_device_features.tessellationShader == VK_FALSE) { | ||
spdlog::warn("Can't add VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT to " | ||
"pipeline statistics flag bit!"); | ||
spdlog::warn( | ||
"Tesselation shaders are not available on this gpu (device_features.tessellationShader = false)"); | ||
break; | ||
} else { | ||
// Tesselation shaders are available so it's safe to add this flag. | ||
ret_val.emplace_back(VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT); | ||
} | ||
break; | ||
case VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT: | ||
if (m_device_features.geometryShader == VK_FALSE) { | ||
spdlog::warn("Can't add VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT to pipeline " | ||
"statistics flag bit!"); | ||
spdlog::warn("Geometry shaders are not available on this gpu (device_features.geometryShader = false)"); | ||
break; | ||
} else { | ||
// Geometry shaders are available so it's safe to add this flag. | ||
ret_val.emplace_back(VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT); | ||
} | ||
break; | ||
case VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT: | ||
if (m_device_features.geometryShader == VK_FALSE) { | ||
spdlog::warn("Can't add VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT to pipeline " | ||
"statistics flag bit!"); | ||
spdlog::warn("Geometry shaders are not available on this gpu (device_features.geometryShader = false)"); | ||
break; | ||
} else { | ||
// Geometry shaders are available so it's safe to add this flag. | ||
ret_val.emplace_back(VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT); | ||
} | ||
break; | ||
default: | ||
// No special check required for this flag. | ||
ret_val.emplace_back(flag_bit); | ||
break; | ||
} | ||
} | ||
|
||
ret_val.shrink_to_fit(); | ||
return ret_val; | ||
} | ||
|
||
// TODO: Make this a as_string method and return a std::string_view from representation.cpp! | ||
std::string QueryPool::get_pipeline_stats_flag_bit_name(const VkQueryPipelineStatisticFlagBits bit) { | ||
switch (bit) { | ||
case VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT: | ||
return "Input assembly vertex count"; | ||
case VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT: | ||
return "Input assembly primitives count"; | ||
case VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT: | ||
return "Vertex shader invocations"; | ||
case VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT: | ||
return "Clipping stage primitives processed"; | ||
case VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT: | ||
return "Clipping stage primitives output"; | ||
case VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT: | ||
return "Fragment shader invocations"; | ||
case VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT: | ||
return "Geometry shader invocations"; | ||
case VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT: | ||
return "Geometry assembly primitives count"; | ||
case VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT: | ||
return "Tessellation control shader patch invocations"; | ||
case VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT: | ||
return "Tessellation evaluation shader invocations"; | ||
case VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT: | ||
return "Compute shader invocations"; | ||
default: | ||
break; | ||
} | ||
return "Unknown"; | ||
} | ||
|
||
QueryPool::QueryPool(const Device &device, const std::string &name) | ||
: QueryPool(device, name, default_pipeline_stats_flag_bits) {} | ||
|
||
QueryPool::QueryPool(const Device &device, const std::string &name, | ||
const std::vector<VkQueryPipelineStatisticFlagBits> &pipeline_stats_flag_bits) | ||
: m_device(device) { | ||
assert(m_device.device()); | ||
assert(!name.empty()); | ||
|
||
// We must first check if pipeline query statistics are available. | ||
vkGetPhysicalDeviceFeatures(m_device.physical_device(), &m_device_features); | ||
|
||
if (m_device_features.pipelineStatisticsQuery == VK_FALSE) { | ||
throw InexorException("Error: vkGetPhysicalDeviceFeatures shows pipelineStatisticsQuery is not supported"); | ||
} | ||
|
||
// Compose pipeline stats flags from pipeline_stats_flag_bits. | ||
const std::vector<VkQueryPipelineStatisticFlagBits> valid_pipeline_stats_flag_bits{ | ||
validate_pipeline_stats_flag_bits(pipeline_stats_flag_bits)}; | ||
|
||
m_pipeline_stat_names.reserve(valid_pipeline_stats_flag_bits.size()); | ||
|
||
VkQueryPipelineStatisticFlags pipeline_stats_flags{}; | ||
|
||
for (const auto valid_bit : valid_pipeline_stats_flag_bits) { | ||
pipeline_stats_flags |= valid_bit; | ||
m_pipeline_stat_names.emplace_back(get_pipeline_stats_flag_bit_name(valid_bit)); | ||
} | ||
|
||
auto query_pool_ci = make_info<VkQueryPoolCreateInfo>(); | ||
|
||
query_pool_ci.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS; | ||
query_pool_ci.pipelineStatistics = pipeline_stats_flags; | ||
query_pool_ci.queryCount = static_cast<std::uint32_t>(valid_pipeline_stats_flag_bits.size()); | ||
|
||
if (const auto result = vkCreateQueryPool(m_device.device(), &query_pool_ci, nullptr, &m_query_pool); | ||
result != VK_SUCCESS) { | ||
throw VulkanException("Error: vkCreateQueryPool failed!", result); | ||
} | ||
} | ||
|
||
void QueryPool::reset(const wrapper::CommandBuffer &cmd_buffer) const { | ||
assert(!m_pipeline_stats.empty()); | ||
vkCmdResetQueryPool(cmd_buffer.get(), m_query_pool, 0, static_cast<std::uint32_t>(m_pipeline_stats.size())); | ||
} | ||
|
||
void QueryPool::begin(const wrapper::CommandBuffer &cmd_buffer) const { | ||
vkCmdBeginQuery(cmd_buffer.get(), m_query_pool, 0, 0); | ||
} | ||
|
||
void QueryPool::end(const wrapper::CommandBuffer &cmd_buffer) const { | ||
vkCmdEndQuery(cmd_buffer.get(), m_query_pool, 0); | ||
} | ||
|
||
void QueryPool::get_results() { | ||
vkGetQueryPoolResults(m_device.device(), m_query_pool, 0, 1, | ||
static_cast<std::uint32_t>(m_pipeline_stats.size()) * sizeof(std::uint64_t), | ||
m_pipeline_stats.data(), sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); | ||
} | ||
|
||
void QueryPool::print_results() const { | ||
for (std::size_t i = 0; i < m_pipeline_stats.size(); i++) { | ||
spdlog::info("{}: {}", m_pipeline_stat_names[i], m_pipeline_stats[i]); | ||
} | ||
} | ||
|
||
QueryPool::~QueryPool() { | ||
vkDestroyQueryPool(m_device.device(), m_query_pool, nullptr); | ||
} | ||
|
||
} // namespace inexor::vulkan_renderer::wrapper |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.