1# Queries
2
3OpenGL queries generally have a straightforward mapping to Vulkan queries, with the exception of
4`GL_PRIMITIVES_GENERATED`.  Some Vulkan queries are active only inside a render pass, while others
5are affected by both inside and outside render pass commands.
6
7## Outside Render Pass Queries
8
9The following queries are recorded outside a render pass.  If a render pass is active when
10`begin()` or `end()` is called for these queries, it will be broken.
11
12- `GL_TIME_ELAPSED_EXT`
13- `GL_TIMESTAMP_EXT`
14
15## Inside Render Pass Queries
16
17The rest of the queries are active only inside render passes.  The context (`ContextVk`) keeps track
18of currently active "render pass queries" and automatically pauses and resumes them as render passes
19are broken and started again.
20
21- On query `begin()`: `ContextVk::beginRenderPassQuery()` is called which:
22  * If a render pass is open, immediately starts the query and keeps track of it
23  * Otherwise keeps the query tracked to be started in the next render pass
24- On query `end()`: `ContextVk::endRenderPassQuery()` is called which:
25  * If a render pass is open, stops the query
26  * Loses track of the query
27- On render pass start: `ContextVk::resumeRenderPassQueriesIfActive()` is called which starts all
28  active queries.
29- On render pass end: `ContextVk::pauseRenderPassQueriesIfActive()` is called which stops all
30  active queries.
31
32In Vulkan, a query cannot be paused or resumed, only begun and ended.  This means that GL queries
33that span multiple render passes must use multiple Vulkan queries whose results are accumulated.
34This is done on render pass start, where `QueryVk::onRenderPassStart()` would stash the previous
35Vulkan query (if any) and create a new one before starting it.  When a query is begun, the
36`QueryVk`'s "current" Vulkan query (`mQueryHelper`) is only allocated if there's a render pass
37active.
38
39**Invariant rule**: With the above algorithm, `QueryVk::mQueryHelper` is at all times either
40`nullptr` or has commands recorded.  This is important when getting query results to be able to
41ask `mQueryHelper` for availability of results.
42
43Later on, `QueryVk::getResult()` would take the sum of the current and all stashed Vulkan queries as
44the final result.
45
46### Mid-Render-Pass Clears
47
48If a clear is performed while a render pass query is active and if that clear needs to take a
49draw-based path, `UtilsVk` ensures that the draw call does not contribute to query results.  This is
50done by pausing (`ContextVk::pauseRenderPassQueriesIfActive`) the queries before the draw call and
51resuming (`ContextVk::resumeRenderPassQueriesIfActive`) afterwards.
52
53The rest of the `UtilsVk` draw-based functions start a render pass out of the knowledge of
54`ContextVk`, so queries will not be activated.  In the future, if any `UtilsVk` functions use the
55current render pass the way `UtilsVk::clearFramebuffer` does, they must also ensure that they pause
56and resume queries.
57
58### Transform Feedback Queries
59
60OpenGL has two distinct queries `GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN` and
61`GL_PRIMITIVES_GENERATED`.  In Vulkan however, these are served by a single query from
62`VK_EXT_transform_feedback`.  Additionally, Vulkan requires that only a single query of any type can
63be active at a time.  This forces ANGLE to have the two GL queries share their Vulkan queries when
64both transform feedback queries are active.
65
66To support the above use case, `QueryVk`s keep ref-counted (`vk::Shared`) Vulkan query
67(`vk::QueryHelper`) objects.  When either transform feedback query is begun:
68
69- If the other transform feedback query (`shareQuery`) is active and a render pass is active:
70  * `shareQuery`'s current Vulkan query is stopped and stashed, and a new one is allocated
71  * `shareQuery`'s new Vulkan query is taken as this query's current one with the ref-count
72     incremented
73  * The Vulkan query is started as usual
74- If the other transform feedback query is active and a render pass is not:
75  * The current Vulkan query is kept `nullptr`.  When the next render pass starts, they will share
76    their Vulkan queries.
77- If the other transform feedback query is not active, proceed as usual
78
79Breaking the `shareQuery`'s Vulkan query on begin ensures that whatever results it may have accrued
80before do not contribute to this query.
81
82Similarly, when a transform feedback query is ended, the Vulkan query is ended as usual and then:
83
84- If the other transform feedback query is active and a render pass is active:
85  * `shareQuery`'s Vulkan query (which is the same as this query's, as they share it) is stashed
86  * `shareQuery` allocates a new Vulkan query and starts it
87
88When a render pass is broken and active queries are paused
89(`ContextVk::pauseRenderPassQueriesIfActive`), only one of the queries will close the shared Vulkan
90query.
91
92When a render pass is started and active queries are resumed
93(`ContextVk::resumeRenderPassQueriesIfActive`), only one of the queries will allocate and start a
94new Vulkan query.  The other one will just take that and share it (and increment the ref-count).
95
96The above solution supports the following scenarios:
97
98- Simultaneous begin of the two queries:
99
100```
101glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
102glBeginQuery(GL_PRIMITIVES_GENERATED)
103glDraw*()
104```
105
106- Simultaneous end of the two queries:
107
108```
109glDraw*()
110glEndQuery(GL_PRIMITIVES_GENERATED)
111glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
112```
113
114- Draw calls between begin calls:
115
116```
117glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
118glDraw*()
119glBeginQuery(GL_PRIMITIVES_GENERATED)
120glDraw*()
121```
122
123- Draw calls between end calls:
124
125```
126glDraw*()
127glEndQuery(GL_PRIMITIVES_GENERATED)
128glDraw*()
129glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
130```
131
132- Queries getting deleted / rebound when the other is active, for example:
133
134```
135glDraw*()
136glEndQuery(GL_PRIMITIVES_GENERATED)
137glDeleteQueries(primitivesGenerated)
138glDraw*()
139glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
140```
141