OpenGL queries generally have a straightforward mapping to Vulkan queries, with the exception of GL_PRIMITIVES_GENERATED
. Some Vulkan queries are active only inside a render pass, while others are affected by both inside and outside render pass commands.
The following queries are recorded outside a render pass. If a render pass is active when begin()
or end()
is called for these queries, it will be broken.
GL_TIME_ELAPSED_EXT
GL_TIMESTAMP_EXT
The rest of the queries are active only inside render passes. The context (ContextVk
) keeps track of currently active “render pass queries” and automatically pauses and resumes them as render passes are broken and started again.
begin()
: ContextVk::beginRenderPassQuery()
is called which:end()
: ContextVk::endRenderPassQuery()
is called which:ContextVk::resumeRenderPassQueriesIfActive()
is called which starts all active queries.ContextVk::pauseRenderPassQueriesIfActive()
is called which stops all active queries.In Vulkan, a query cannot be paused or resumed, only begun and ended. This means that GL queries that span multiple render passes must use multiple Vulkan queries whose results are accumulated. This is done on render pass start, where QueryVk::onRenderPassStart()
would stash the previous Vulkan query (if any) and create a new one before starting it. When a query is begun, the QueryVk
‘s “current” Vulkan query (mQueryHelper
) is only allocated if there’s a render pass active.
Invariant rule: With the above algorithm, QueryVk::mQueryHelper
is at all times either nullptr
or has commands recorded. This is important when getting query results to be able to ask mQueryHelper
for availability of results.
Later on, QueryVk::getResult()
would take the sum of the current and all stashed Vulkan queries as the final result.
If a clear is performed while a render pass query is active and if that clear needs to take a draw-based path, UtilsVk
ensures that the draw call does not contribute to query results. This is done by pausing (ContextVk::pauseRenderPassQueriesIfActive
) the queries before the draw call and resuming (ContextVk::resumeRenderPassQueriesIfActive
) afterwards.
The rest of the UtilsVk
draw-based functions start a render pass out of the knowledge of ContextVk
, so queries will not be activated. In the future, if any UtilsVk
functions use the current render pass the way UtilsVk::clearFramebuffer
does, they must also ensure that they pause and resume queries.
OpenGL has two distinct queries GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
and GL_PRIMITIVES_GENERATED
. In Vulkan however, these are served by a single query from VK_EXT_transform_feedback
. Additionally, Vulkan requires that only a single query of any type can be active at a time. This forces ANGLE to have the two GL queries share their Vulkan queries when both transform feedback queries are active.
To support the above use case, QueryVk
s keep ref-counted (vk::Shared
) Vulkan query (vk::QueryHelper
) objects. When either transform feedback query is begun:
shareQuery
) is active and a render pass is active:shareQuery
's current Vulkan query is stopped and stashed, and a new one is allocatedshareQuery
‘s new Vulkan query is taken as this query’s current one with the ref-count incrementednullptr
. When the next render pass starts, they will share their Vulkan queries.Breaking the shareQuery
's Vulkan query on begin ensures that whatever results it may have accrued before do not contribute to this query.
Similarly, when a transform feedback query is ended, the Vulkan query is ended as usual and then:
shareQuery
‘s Vulkan query (which is the same as this query’s, as they share it) is stashedshareQuery
allocates a new Vulkan query and starts itWhen a render pass is broken and active queries are paused (ContextVk::pauseRenderPassQueriesIfActive
), only one of the queries will close the shared Vulkan query.
When a render pass is started and active queries are resumed (ContextVk::resumeRenderPassQueriesIfActive
), only one of the queries will allocate and start a new Vulkan query. The other one will just take that and share it (and increment the ref-count).
The above solution supports the following scenarios:
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) glBeginQuery(GL_PRIMITIVES_GENERATED) glDraw*()
glDraw*() glEndQuery(GL_PRIMITIVES_GENERATED) glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) glDraw*() glBeginQuery(GL_PRIMITIVES_GENERATED) glDraw*()
glDraw*() glEndQuery(GL_PRIMITIVES_GENERATED) glDraw*() glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
glDraw*() glEndQuery(GL_PRIMITIVES_GENERATED) glDeleteQueries(primitivesGenerated) glDraw*() glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)