zink: reset query on-demand when beginning a new query from resume
[mesa.git] / src / gallium / drivers / zink / zink_query.c
1 #include "zink_query.h"
2
3 #include "zink_context.h"
4 #include "zink_resource.h"
5 #include "zink_screen.h"
6
7 #include "util/u_dump.h"
8 #include "util/u_inlines.h"
9 #include "util/u_memory.h"
10
11 struct zink_query {
12 enum pipe_query_type type;
13
14 VkQueryPool query_pool;
15 unsigned curr_query, num_queries;
16
17 VkQueryType vkqtype;
18 bool use_64bit;
19 bool precise;
20
21 struct list_head active_list;
22 };
23
24 static VkQueryType
25 convert_query_type(unsigned query_type, bool *use_64bit, bool *precise)
26 {
27 *use_64bit = false;
28 *precise = false;
29 switch (query_type) {
30 case PIPE_QUERY_OCCLUSION_COUNTER:
31 *precise = true;
32 *use_64bit = true;
33 case PIPE_QUERY_OCCLUSION_PREDICATE:
34 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
35 return VK_QUERY_TYPE_OCCLUSION;
36 case PIPE_QUERY_TIMESTAMP:
37 *use_64bit = true;
38 return VK_QUERY_TYPE_TIMESTAMP;
39 case PIPE_QUERY_PIPELINE_STATISTICS:
40 return VK_QUERY_TYPE_PIPELINE_STATISTICS;
41 default:
42 debug_printf("unknown query: %s\n",
43 util_str_query_type(query_type, true));
44 unreachable("zink: unknown query type");
45 }
46 }
47
48 static struct pipe_query *
49 zink_create_query(struct pipe_context *pctx,
50 unsigned query_type, unsigned index)
51 {
52 struct zink_screen *screen = zink_screen(pctx->screen);
53 struct zink_query *query = CALLOC_STRUCT(zink_query);
54 VkQueryPoolCreateInfo pool_create = {};
55
56 if (!query)
57 return NULL;
58
59 query->type = query_type;
60 query->vkqtype = convert_query_type(query_type, &query->use_64bit, &query->precise);
61 if (query->vkqtype == -1)
62 return NULL;
63
64 query->num_queries = query_type == PIPE_QUERY_TIMESTAMP ? 1 : 100;
65 query->curr_query = 0;
66
67 pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
68 pool_create.queryType = query->vkqtype;
69 pool_create.queryCount = query->num_queries;
70
71 VkResult status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->query_pool);
72 if (status != VK_SUCCESS) {
73 FREE(query);
74 return NULL;
75 }
76 return (struct pipe_query *)query;
77 }
78
79 /* TODO: rework this to be less hammer-ish using deferred destroy */
80 static void
81 wait_query(struct pipe_context *pctx, struct zink_query *query)
82 {
83 struct pipe_fence_handle *fence = NULL;
84
85 pctx->flush(pctx, &fence, PIPE_FLUSH_HINT_FINISH);
86 if (fence) {
87 pctx->screen->fence_finish(pctx->screen, NULL, fence,
88 PIPE_TIMEOUT_INFINITE);
89 pctx->screen->fence_reference(pctx->screen, &fence, NULL);
90 }
91 }
92
93 static void
94 zink_destroy_query(struct pipe_context *pctx,
95 struct pipe_query *q)
96 {
97 struct zink_screen *screen = zink_screen(pctx->screen);
98 struct zink_query *query = (struct zink_query *)q;
99
100 if (!list_is_empty(&query->active_list)) {
101 wait_query(pctx, query);
102 }
103
104 vkDestroyQueryPool(screen->dev, query->query_pool, NULL);
105 FREE(query);
106 }
107
108 static void
109 begin_query(struct zink_context *ctx, struct zink_query *q)
110 {
111 VkQueryControlFlags flags = 0;
112 if (q->precise)
113 flags |= VK_QUERY_CONTROL_PRECISE_BIT;
114
115 struct zink_batch *batch = zink_curr_batch(ctx);
116 vkCmdBeginQuery(batch->cmdbuf, q->query_pool, q->curr_query, flags);
117 }
118
119 static bool
120 zink_begin_query(struct pipe_context *pctx,
121 struct pipe_query *q)
122 {
123 struct zink_context *ctx = zink_context(pctx);
124 struct zink_query *query = (struct zink_query *)q;
125
126 /* ignore begin_query for timestamps */
127 if (query->type == PIPE_QUERY_TIMESTAMP)
128 return true;
129
130 /* TODO: resetting on begin isn't ideal, as it forces render-pass exit...
131 * should instead reset on creation (if possible?)... Or perhaps maintain
132 * the pool in the batch instead?
133 */
134 struct zink_batch *batch = zink_batch_no_rp(zink_context(pctx));
135 vkCmdResetQueryPool(batch->cmdbuf, query->query_pool, 0, MIN2(query->curr_query + 1, query->num_queries));
136 query->curr_query = 0;
137
138 begin_query(ctx, query);
139 list_addtail(&query->active_list, &ctx->active_queries);
140
141 return true;
142 }
143
144 static void
145 end_query(struct zink_context *ctx, struct zink_query *q)
146 {
147 struct zink_batch *batch = zink_curr_batch(ctx);
148 assert(q->type != PIPE_QUERY_TIMESTAMP);
149 vkCmdEndQuery(batch->cmdbuf, q->query_pool, q->curr_query);
150 if (++q->curr_query == q->num_queries) {
151 assert(0);
152 /* need to reset pool! */
153 }
154 }
155
156 static bool
157 zink_end_query(struct pipe_context *pctx,
158 struct pipe_query *q)
159 {
160 struct zink_context *ctx = zink_context(pctx);
161 struct zink_query *query = (struct zink_query *)q;
162
163 if (query->type == PIPE_QUERY_TIMESTAMP) {
164 assert(query->curr_query == 0);
165 struct zink_batch *batch = zink_curr_batch(ctx);
166 vkCmdWriteTimestamp(batch->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
167 query->query_pool, 0);
168 } else {
169 end_query(ctx, query);
170 list_delinit(&query->active_list);
171 }
172
173 return true;
174 }
175
176 static bool
177 zink_get_query_result(struct pipe_context *pctx,
178 struct pipe_query *q,
179 bool wait,
180 union pipe_query_result *result)
181 {
182 struct zink_screen *screen = zink_screen(pctx->screen);
183 struct zink_query *query = (struct zink_query *)q;
184 VkQueryResultFlagBits flags = 0;
185
186 if (wait) {
187 wait_query(pctx, query);
188 flags |= VK_QUERY_RESULT_WAIT_BIT;
189 } else
190 pctx->flush(pctx, NULL, 0);
191
192 if (query->use_64bit)
193 flags |= VK_QUERY_RESULT_64_BIT;
194
195 // TODO: handle curr_query > 100
196 // union pipe_query_result results[100];
197 uint64_t results[100];
198 memset(results, 0, sizeof(results));
199 assert(query->curr_query <= ARRAY_SIZE(results));
200 if (vkGetQueryPoolResults(screen->dev, query->query_pool,
201 0, query->curr_query,
202 sizeof(results),
203 results,
204 sizeof(uint64_t),
205 flags) != VK_SUCCESS)
206 return false;
207
208 util_query_clear_result(result, query->type);
209 for (int i = 0; i < query->curr_query; ++i) {
210 switch (query->type) {
211 case PIPE_QUERY_OCCLUSION_PREDICATE:
212 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
213 case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
214 case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
215 case PIPE_QUERY_GPU_FINISHED:
216 result->b |= results[i] != 0;
217 break;
218
219 case PIPE_QUERY_OCCLUSION_COUNTER:
220 result->u64 += results[i];
221 break;
222
223 default:
224 debug_printf("unhangled query type: %s\n",
225 util_str_query_type(query->type, true));
226 unreachable("unexpected query type");
227 }
228 }
229
230 return TRUE;
231 }
232
233 void
234 zink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch)
235 {
236 struct zink_query *query;
237 LIST_FOR_EACH_ENTRY(query, &ctx->active_queries, active_list) {
238 end_query(ctx, query);
239 }
240 }
241
242 void
243 zink_resume_queries(struct zink_context *ctx, struct zink_batch *batch)
244 {
245 struct zink_query *query;
246 LIST_FOR_EACH_ENTRY(query, &ctx->active_queries, active_list) {
247 vkCmdResetQueryPool(batch->cmdbuf, query->query_pool, query->curr_query, 1);
248 begin_query(ctx, query);
249 }
250 }
251
252 static void
253 zink_set_active_query_state(struct pipe_context *pctx, bool enable)
254 {
255 struct zink_context *ctx = zink_context(pctx);
256 ctx->queries_disabled = !enable;
257
258 struct zink_batch *batch = zink_curr_batch(ctx);
259 if (ctx->queries_disabled)
260 zink_suspend_queries(ctx, batch);
261 else
262 zink_resume_queries(ctx, batch);
263 }
264
265 static void
266 zink_render_condition(struct pipe_context *pctx,
267 struct pipe_query *pquery,
268 bool condition,
269 enum pipe_render_cond_flag mode)
270 {
271 struct zink_context *ctx = zink_context(pctx);
272 struct zink_screen *screen = zink_screen(pctx->screen);
273 struct zink_query *query = (struct zink_query *)pquery;
274 struct zink_batch *batch = zink_curr_batch(ctx);
275 VkQueryResultFlagBits flags = 0;
276
277 if (query == NULL) {
278 screen->vk_CmdEndConditionalRenderingEXT(batch->cmdbuf);
279 return;
280 }
281
282 struct pipe_resource *pres;
283 struct zink_resource *res;
284 struct pipe_resource templ = {};
285 templ.width0 = 8;
286 templ.height0 = 1;
287 templ.depth0 = 1;
288 templ.format = PIPE_FORMAT_R8_UINT;
289 templ.target = PIPE_BUFFER;
290
291 /* need to create a vulkan buffer to copy the data into */
292 pres = pctx->screen->resource_create(pctx->screen, &templ);
293 if (!pres)
294 return;
295
296 res = (struct zink_resource *)pres;
297
298 if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT)
299 flags |= VK_QUERY_RESULT_WAIT_BIT;
300
301 if (query->use_64bit)
302 flags |= VK_QUERY_RESULT_64_BIT;
303 vkCmdCopyQueryPoolResults(batch->cmdbuf, query->query_pool, 0, 1,
304 res->buffer, 0, 0, flags);
305
306 VkConditionalRenderingFlagsEXT begin_flags = 0;
307 if (condition)
308 begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
309 VkConditionalRenderingBeginInfoEXT begin_info = {};
310 begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
311 begin_info.buffer = res->buffer;
312 begin_info.flags = begin_flags;
313 screen->vk_CmdBeginConditionalRenderingEXT(batch->cmdbuf, &begin_info);
314
315 zink_batch_reference_resoure(batch, res);
316
317 pipe_resource_reference(&pres, NULL);
318 }
319
320 void
321 zink_context_query_init(struct pipe_context *pctx)
322 {
323 struct zink_context *ctx = zink_context(pctx);
324 list_inithead(&ctx->active_queries);
325
326 pctx->create_query = zink_create_query;
327 pctx->destroy_query = zink_destroy_query;
328 pctx->begin_query = zink_begin_query;
329 pctx->end_query = zink_end_query;
330 pctx->get_query_result = zink_get_query_result;
331 pctx->set_active_query_state = zink_set_active_query_state;
332 pctx->render_condition = zink_render_condition;
333 }