The VK_KHR_performance_query extension adds a mechanism to allow
querying of performance counters for use in applications and by
profiling tools.
Each queue family may expose counters that can be enabled on a queue
of that family. We extend QueryType to
add a new query type for performance queries, and chain a structure on
QueryPoolCreateInfo to specify the performance
queries to enable.
1) Should this extension include a mechanism to begin a query in command
buffer A and end the query in command buffer B?
RESOLVED No - queries are tied to command buffer creation and thus
have to be encapsulated within a single command buffer.
2) Should this extension include a mechanism to begin and end queries
globally on the queue, not using the existing command buffer commands?
RESOLVED No - for the same reasoning as the resolution of 1).
3) Should this extension expose counters that require multiple passes?
RESOLVED Yes - users should re-submit a command buffer with the same
commands in it multiple times, specifying the pass to count as the query
parameter in VkPerformanceQuerySubmitInfoKHR.
4) How to handle counters across parallel workloads?
RESOLVED Secondary command buffers inherit any counter pass index
specified in the parent primary command buffer. Note: this is no longer
an issue after change from issue 10 resolution
6) What commands does the profiling lock have to be held for?
RESOLVED For any command buffer that is being queried with a
performance query pool, the profiling lock must be held while that
command buffer is in the recording, executable, or pending state.
8) Should we allow performance queries to interact with multiview?
RESOLVED Yes, but the performance queries must be performed once for
each pass per view.
9) Should a queryCount > 1 be usable for performance queries?
RESOLVED Yes. Some vendors will have costly performance counter
query pool creation, and would rather if a certain set of counters were
to be used multiple times that a queryCount > 1 can be used to
amortize the instantiation cost.
10) Should we introduce an indirect mechanism to set the counter pass
index?
RESOLVED Specify the counter pass index at submit time instead, to
avoid requiring re-recording of command buffers when multiple counter
passes are needed.
Examples
The following example shows how to find what performance counters a
queue family supports, setup a query pool to record these performance
counters, how to add the query pool to the command buffer to record
information, and how to get the results from the query pool.
// A previously created physical device
VkPhysicalDevice physicalDevice;
// One of the queue families our device supports
uint32_t queueFamilyIndex;
uint32_t counterCount;
// Get the count of counters supported
vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR(
physicalDevice,
queueFamilyIndex,
&counterCount,
NULL,
NULL);
VkPerformanceCounterKHR* counters =
malloc(sizeof(VkPerformanceCounterKHR) * counterCount);
VkPerformanceCounterDescriptionKHR* counterDescriptions =
malloc(sizeof(VkPerformanceCounterDescriptionKHR) * counterCount);
// Get the counters supported
vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR(
physicalDevice,
queueFamilyIndex,
&counterCount,
counters,
counterDescriptions);
// Try to enable the first 8 counters
uint32_t enabledCounters[8];
const uint32_t enabledCounterCount = min(counterCount, 8));
for (uint32_t i = 0; i < enabledCounterCount; i++) {
enabledCounters[i] = i;
}
// A previously created device that had the performanceCounterQueryPools feature
// set to VK_TRUE
VkDevice device;
VkQueryPoolPerformanceCreateInfoKHR performanceQueryCreateInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_CREATE_INFO_KHR,
.pNext = NULL,
// Specify the queue family that this performance query is performed on
.queueFamilyIndex = queueFamilyIndex,
// The number of counters to enable
.counterIndexCount = enabledCounterCount,
// The array of indices of counters to enable
.pCounterIndices = enabledCounters
};
// Get the number of passes our counters will require.
uint32_t numPasses;
vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR(
physicalDevice,
&performanceQueryCreateInfo,
&numPasses);
VkQueryPoolCreateInfo queryPoolCreateInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.pNext = &performanceQueryCreateInfo,
.flags = 0,
// Using our new query type here
.queryType = VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR,
.queryCount = 1,
.pipelineStatistics = 0
};
VkQueryPool queryPool;
VkResult result = vkCreateQueryPool(
device,
&queryPoolCreateInfo,
NULL,
&queryPool);
assert(VK_SUCCESS == result);
// A queue from queueFamilyIndex
VkQueue queue;
// A command buffer we want to record counters on
VkCommandBuffer commandBuffer;
VkCommandBufferBeginInfo commandBufferBeginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = NULL,
.flags = 0,
.pInheritanceInfo = NULL
};
VkAcquireProfilingLockInfoKHR lockInfo = {
.sType = VK_STRUCTURE_TYPE_ACQUIRE_PROFILING_LOCK_INFO_KHR,
.pNext = NULL,
.flags = 0,
.timeout = UINT64_MAX // Wait forever for the lock
};
// Acquire the profiling lock before we record command buffers
// that will use performance queries
result = vkAcquireProfilingLockKHR(device, &lockInfo);
assert(VK_SUCCESS == result);
result = vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo);
assert(VK_SUCCESS == result);
vkCmdResetQueryPool(
commandBuffer,
queryPool,
0,
1);
vkCmdBeginQuery(
commandBuffer,
queryPool,
0,
0);
// Perform the commands you want to get performance information on
// ...
// Perform a barrier to ensure all previous commands were complete before
// ending the query
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
NULL,
0,
NULL,
0,
NULL);
vkCmdEndQuery(
commandBuffer,
queryPool,
0);
result = vkEndCommandBuffer(commandBuffer);
assert(VK_SUCCESS == result);
for (uint32_t counterPass = 0; counterPass < numPasses; counterPass++) {
VkPerformanceQuerySubmitInfoKHR performanceQuerySubmitInfo = {
VK_STRUCTURE_TYPE_PERFORMANCE_QUERY_SUBMIT_INFO_KHR,
NULL,
counterPass
};
// Submit the command buffer and wait for its completion
// ...
}
// Release the profiling lock after the command buffer is no longer in the
// pending state.
vkReleaseProfilingLockKHR(device);
result = vkResetCommandBuffer(commandBuffer, 0);
assert(VK_SUCCESS == result);
// Create an array to hold the results of all counters
VkPerformanceCounterResultKHR* recordedCounters = malloc(
sizeof(VkPerformanceCounterResultKHR) * enabledCounterCount);
result = vkGetQueryPoolResults(
device,
queryPool,
0,
1,
sizeof(VkPerformanceCounterResultKHR) * enabledCounterCount,
recordedCounters,
sizeof(VkPerformanceCounterResultKHR) * enabledCounterCount,
NULL);
// recordedCounters is filled with our counters, we will look at one for posterity
switch (counters[0].storage) {
case VK_PERFORMANCE_COUNTER_STORAGE_INT32:
// use recordCounters[0].int32 to get at the counter result!
break;
case VK_PERFORMANCE_COUNTER_STORAGE_INT64:
// use recordCounters[0].int64 to get at the counter result!
break;
case VK_PERFORMANCE_COUNTER_STORAGE_UINT32:
// use recordCounters[0].uint32 to get at the counter result!
break;
case VK_PERFORMANCE_COUNTER_STORAGE_UINT64:
// use recordCounters[0].uint64 to get at the counter result!
break;
case VK_PERFORMANCE_COUNTER_STORAGE_FLOAT32:
// use recordCounters[0].float32 to get at the counter result!
break;
case VK_PERFORMANCE_COUNTER_STORAGE_FLOAT64:
// use recordCounters[0].float64 to get at the counter result!
break;
}
vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR -
Reports properties of the performance query counters available on a
queue family of a device
Description
If pCounters is NULL and pCounterDescriptions is NULL, then the
number of counters available is returned in pCounterCount. Otherwise,
pCounterCountmust point to a variable set by the application to the
number of elements in the pCounters, pCounterDescriptions, or both
arrays and on return the variable is overwritten with the number of
structures actually written out. If pCounterCount is less than the
number of counters available, at most pCounterCount structures will be
written, and INCOMPLETE will be returned
instead of SUCCESS, to indicate that not
all the available counters were returned.
pCounterCountmust be a valid pointer to a uint32_t value
If the value referenced by pCounterCount is not 0, and
pCounters is not NULL, pCountersmust be a valid pointer to
an array of pCounterCountPerformanceCounterKHR structures
If the value referenced by pCounterCount is not 0, and
pCounterDescriptions is not NULL, pCounterDescriptionsmust
be a valid pointer to an array of pCounterCountPerformanceCounterDescriptionKHR structures
vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR - Reports the
number of passes require for a performance query pool type
Description
The pPerformanceQueryCreateInfo member
QueryPoolPerformanceCreateInfoKHR::queueFamilyIndexmust be a
queue family of physicalDevice. The number of passes required to
capture the counters specified in the pPerformanceQueryCreateInfo
member QueryPoolPerformanceCreateInfoKHR::pCounters is returned in
pNumPasses.
performanceCounterMultipleQueryPools indicates whether the
implementation supports using multiple performance query pools in a
primary command buffer and secondary command buffers executed within it.
Instances6Eq, Show, Storable, FromCStruct, ToCStruct, Zero
VkAcquireProfilingLockInfoKHR - Structure specifying parameters to
acquire the profiling lock
Valid Usage (Implicit)
If timeout is 0, acquireProfilingLockKHR will not block while
attempting to acquire the profiling lock. If timeout is UINT64_MAX,
the function will not return until the profiling lock was acquired.
counterPassIndex specifies which counter pass index is active.
counterPassIndexmust be less than the number of counter passes
required by any queries within the batch. The required number of counter
passes for a performance query is obtained by calling
getPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR
Instances6Eq, Show, Storable, FromCStruct, ToCStruct, Zero
PERFORMANCE_COUNTER_SCOPE_RENDER_PASS_KHR - the performance
counter scope is zero or more complete render passes. The
performance query containing the performance counter must begin
and end outside a render pass instance.