1 #include "zink_query.h"
2
3 #include "zink_context.h"
4 #include "zink_fence.h"
5 #include "zink_program.h"
6 #include "zink_resource.h"
7 #include "zink_screen.h"
8
9 #include "util/hash_table.h"
10 #include "util/set.h"
11 #include "util/u_dump.h"
12 #include "util/u_inlines.h"
13 #include "util/u_memory.h"
14
15 #if defined(PIPE_ARCH_X86_64) || defined(PIPE_ARCH_PPC_64) || defined(PIPE_ARCH_AARCH64) || defined(PIPE_ARCH_MIPS64)
16 #define NUM_QUERIES 5000
17 #else
18 #define NUM_QUERIES 500
19 #endif
20
21 struct zink_query_pool {
22 struct list_head list;
23 VkQueryType vk_query_type;
24 VkQueryPipelineStatisticFlags pipeline_stats;
25 VkQueryPool query_pool;
26 unsigned last_range;
27 };
28
29 struct zink_query_buffer {
30 struct list_head list;
31 unsigned num_results;
32 struct pipe_resource *buffers[PIPE_MAX_VERTEX_STREAMS];
33 };
34
35 struct zink_vk_query {
36 struct zink_query_pool *pool;
37 unsigned query_id;
38 bool needs_reset;
39 bool started;
40 uint32_t refcount;
41 };
42
43 struct zink_query_start {
44 struct zink_vk_query *vkq[PIPE_MAX_VERTEX_STREAMS];
45 bool have_gs;
46 bool have_xfb;
47 bool was_line_loop;
48 };
49
50 struct zink_query {
51 struct threaded_query base;
52 enum pipe_query_type type;
53
54 struct zink_query_pool *pool[2];
55
56 /* Everytime the gallium query needs
57 * another vulkan query, add a new start.
58 */
59 struct util_dynarray starts;
60
61 unsigned last_start_idx;
62 VkQueryType vkqtype;
63 unsigned index;
64 bool precise;
65
66 bool active; /* query is considered active by vk */
67 bool needs_reset; /* query is considered active by vk and cannot be destroyed */
68 bool dead; /* query should be destroyed when its fence finishes */
69 bool needs_update; /* query needs to update its qbos */
70 bool needs_rast_discard_workaround; /* query needs discard disabled */
71
72 struct list_head active_list;
73
74 struct list_head stats_list; /* when active, statistics queries are added to ctx->primitives_generated_queries */
75 bool has_draws; /* have_gs and have_xfb are valid for idx=curr_query */
76
77 struct zink_batch_usage *batch_uses; //batch that the query was started in
78
79 struct list_head buffers;
80 union {
81 struct zink_query_buffer *curr_qbo;
82 struct pipe_fence_handle *fence; //PIPE_QUERY_GPU_FINISHED
83 };
84
85 struct zink_resource *predicate;
86 bool predicate_dirty;
87 };
88
89 static inline int
get_num_starts(struct zink_query * q)90 get_num_starts(struct zink_query *q)
91 {
92 return util_dynarray_num_elements(&q->starts, struct zink_query_start);
93 }
94
95 static void
96 update_query_id(struct zink_context *ctx, struct zink_query *q);
97
98 static void
begin_vk_query_indexed(struct zink_context * ctx,struct zink_vk_query * vkq,int index,VkQueryControlFlags flags)99 begin_vk_query_indexed(struct zink_context *ctx, struct zink_vk_query *vkq, int index,
100 VkQueryControlFlags flags)
101 {
102 struct zink_batch *batch = &ctx->batch;
103 if (!vkq->started) {
104 VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf,
105 vkq->pool->query_pool,
106 vkq->query_id,
107 flags,
108 index);
109 vkq->started = true;
110 }
111 }
112
113 static void
end_vk_query_indexed(struct zink_context * ctx,struct zink_vk_query * vkq,int index)114 end_vk_query_indexed(struct zink_context *ctx, struct zink_vk_query *vkq, int index)
115 {
116 struct zink_batch *batch = &ctx->batch;
117 if (vkq->started) {
118 VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf,
119 vkq->pool->query_pool,
120 vkq->query_id, index);
121 vkq->started = false;
122 }
123 }
124
125 static void
reset_vk_query_pool(struct zink_context * ctx,struct zink_vk_query * vkq)126 reset_vk_query_pool(struct zink_context *ctx, struct zink_vk_query *vkq)
127 {
128 struct zink_batch *batch = &ctx->batch;
129 if (vkq->needs_reset) {
130 VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, vkq->pool->query_pool, vkq->query_id, 1);
131 vkq->needs_reset = false;
132 }
133 }
134
135 void
zink_context_destroy_query_pools(struct zink_context * ctx)136 zink_context_destroy_query_pools(struct zink_context *ctx)
137 {
138 struct zink_screen *screen = zink_screen(ctx->base.screen);
139 list_for_each_entry_safe(struct zink_query_pool, pool, &ctx->query_pools, list) {
140 VKSCR(DestroyQueryPool)(screen->dev, pool->query_pool, NULL);
141 list_del(&pool->list);
142 FREE(pool);
143 }
144 }
145
146 static struct zink_query_pool *
find_or_allocate_qp(struct zink_context * ctx,VkQueryType vk_query_type,VkQueryPipelineStatisticFlags pipeline_stats)147 find_or_allocate_qp(struct zink_context *ctx,
148 VkQueryType vk_query_type,
149 VkQueryPipelineStatisticFlags pipeline_stats)
150 {
151 struct zink_screen *screen = zink_screen(ctx->base.screen);
152 list_for_each_entry(struct zink_query_pool, pool, &ctx->query_pools, list) {
153 if (pool->vk_query_type == vk_query_type) {
154 if (vk_query_type == VK_QUERY_TYPE_PIPELINE_STATISTICS) {
155 if (pool->pipeline_stats == pipeline_stats)
156 return pool;
157 } else
158 return pool;
159 }
160 }
161
162 struct zink_query_pool *new_pool = CALLOC_STRUCT(zink_query_pool);
163 if (!new_pool)
164 return NULL;
165
166 new_pool->vk_query_type = vk_query_type;
167 new_pool->pipeline_stats = pipeline_stats;
168
169 VkQueryPoolCreateInfo pool_create = {0};
170 pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
171 pool_create.queryType = vk_query_type;
172 pool_create.queryCount = NUM_QUERIES;
173 pool_create.pipelineStatistics = pipeline_stats;
174
175 VkResult status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &new_pool->query_pool);
176 if (status != VK_SUCCESS) {
177 mesa_loge("ZINK: vkCreateQueryPool failed (%s)", vk_Result_to_str(status));
178 FREE(new_pool);
179 return NULL;
180 }
181
182 list_addtail(&new_pool->list, &ctx->query_pools);
183 return new_pool;
184 }
185
186 static void
187 update_qbo(struct zink_context *ctx, struct zink_query *q);
188 static void
189 reset_qbos(struct zink_context *ctx, struct zink_query *q);
190
191
192 static bool
is_emulated_primgen(const struct zink_query * q)193 is_emulated_primgen(const struct zink_query *q)
194 {
195 return q->type == PIPE_QUERY_PRIMITIVES_GENERATED &&
196 q->vkqtype != VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT;
197 }
198
199 static inline unsigned
get_num_query_pools(struct zink_query * q)200 get_num_query_pools(struct zink_query *q)
201 {
202 if (is_emulated_primgen(q))
203 return 2;
204 return 1;
205 }
206
207 static inline unsigned
get_num_queries(struct zink_query * q)208 get_num_queries(struct zink_query *q)
209 {
210 if (is_emulated_primgen(q))
211 return 2;
212 if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE)
213 return PIPE_MAX_VERTEX_STREAMS;
214 return 1;
215 }
216
217 static inline unsigned
get_num_results(struct zink_query * q)218 get_num_results(struct zink_query *q)
219 {
220 if (q->vkqtype == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT)
221 return 1;
222 switch (q->type) {
223 case PIPE_QUERY_OCCLUSION_COUNTER:
224 case PIPE_QUERY_OCCLUSION_PREDICATE:
225 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
226 case PIPE_QUERY_TIME_ELAPSED:
227 case PIPE_QUERY_TIMESTAMP:
228 case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
229 return 1;
230 case PIPE_QUERY_PRIMITIVES_GENERATED:
231 case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
232 case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
233 case PIPE_QUERY_PRIMITIVES_EMITTED:
234 return 2;
235 default:
236 debug_printf("unknown query: %s\n",
237 util_str_query_type(q->type, true));
238 unreachable("zink: unknown query type");
239 }
240 }
241
242 static VkQueryPipelineStatisticFlags
pipeline_statistic_convert(enum pipe_statistics_query_index idx)243 pipeline_statistic_convert(enum pipe_statistics_query_index idx)
244 {
245 unsigned map[] = {
246 [PIPE_STAT_QUERY_IA_VERTICES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT,
247 [PIPE_STAT_QUERY_IA_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT,
248 [PIPE_STAT_QUERY_VS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT,
249 [PIPE_STAT_QUERY_GS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT,
250 [PIPE_STAT_QUERY_GS_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT,
251 [PIPE_STAT_QUERY_C_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT,
252 [PIPE_STAT_QUERY_C_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT,
253 [PIPE_STAT_QUERY_PS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT,
254 [PIPE_STAT_QUERY_HS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT,
255 [PIPE_STAT_QUERY_DS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT,
256 [PIPE_STAT_QUERY_CS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT
257 };
258 assert(idx < ARRAY_SIZE(map));
259 return map[idx];
260 }
261
262 static void
timestamp_to_nanoseconds(struct zink_screen * screen,uint64_t * timestamp)263 timestamp_to_nanoseconds(struct zink_screen *screen, uint64_t *timestamp)
264 {
265 /* The number of valid bits in a timestamp value is determined by
266 * the VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is written.
267 * - 17.5. Timestamp Queries
268 */
269 if (screen->timestamp_valid_bits < 64)
270 *timestamp &= (1ull << screen->timestamp_valid_bits) - 1;
271
272 /* The number of nanoseconds it takes for a timestamp value to be incremented by 1
273 * can be obtained from VkPhysicalDeviceLimits::timestampPeriod
274 * - 17.5. Timestamp Queries
275 */
276 *timestamp *= (double)screen->info.props.limits.timestampPeriod;
277 }
278
279 static VkQueryType
convert_query_type(struct zink_screen * screen,enum pipe_query_type query_type,bool * precise)280 convert_query_type(struct zink_screen *screen, enum pipe_query_type query_type, bool *precise)
281 {
282 *precise = false;
283 switch (query_type) {
284 case PIPE_QUERY_OCCLUSION_COUNTER:
285 *precise = true;
286 FALLTHROUGH;
287 case PIPE_QUERY_OCCLUSION_PREDICATE:
288 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
289 return VK_QUERY_TYPE_OCCLUSION;
290 case PIPE_QUERY_TIME_ELAPSED:
291 case PIPE_QUERY_TIMESTAMP:
292 return VK_QUERY_TYPE_TIMESTAMP;
293 case PIPE_QUERY_PRIMITIVES_GENERATED:
294 return screen->info.have_EXT_primitives_generated_query ?
295 VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT :
296 VK_QUERY_TYPE_PIPELINE_STATISTICS;
297 case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
298 return VK_QUERY_TYPE_PIPELINE_STATISTICS;
299 case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
300 case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
301 case PIPE_QUERY_PRIMITIVES_EMITTED:
302 return VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
303 default:
304 debug_printf("unknown query: %s\n",
305 util_str_query_type(query_type, true));
306 unreachable("zink: unknown query type");
307 }
308 }
309
310 static bool
needs_stats_list(struct zink_query * query)311 needs_stats_list(struct zink_query *query)
312 {
313 return is_emulated_primgen(query) ||
314 query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE ||
315 query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE;
316 }
317
318 static bool
is_time_query(struct zink_query * query)319 is_time_query(struct zink_query *query)
320 {
321 return query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIME_ELAPSED;
322 }
323
324 static bool
is_so_overflow_query(struct zink_query * query)325 is_so_overflow_query(struct zink_query *query)
326 {
327 return query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE || query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE;
328 }
329
330 static bool
is_bool_query(struct zink_query * query)331 is_bool_query(struct zink_query *query)
332 {
333 return is_so_overflow_query(query) ||
334 query->type == PIPE_QUERY_OCCLUSION_PREDICATE ||
335 query->type == PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE ||
336 query->type == PIPE_QUERY_GPU_FINISHED;
337 }
338
339 static bool
qbo_append(struct pipe_screen * screen,struct zink_query * query)340 qbo_append(struct pipe_screen *screen, struct zink_query *query)
341 {
342 if (query->curr_qbo && query->curr_qbo->list.next)
343 return true;
344 struct zink_query_buffer *qbo = CALLOC_STRUCT(zink_query_buffer);
345 if (!qbo)
346 return false;
347 int num_buffers = get_num_queries(query);
348
349 for (unsigned i = 0; i < num_buffers; i++) {
350 qbo->buffers[i] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
351 PIPE_USAGE_STAGING,
352 /* this is the maximum possible size of the results in a given buffer */
353 NUM_QUERIES * get_num_results(query) * sizeof(uint64_t));
354 if (!qbo->buffers[i])
355 goto fail;
356 }
357 list_addtail(&qbo->list, &query->buffers);
358
359 return true;
360 fail:
361 for (unsigned i = 0; i < num_buffers; i++)
362 pipe_resource_reference(&qbo->buffers[i], NULL);
363 FREE(qbo);
364 return false;
365 }
366
367 static void
destroy_query(struct zink_screen * screen,struct zink_query * query)368 destroy_query(struct zink_screen *screen, struct zink_query *query)
369 {
370 assert(zink_screen_usage_check_completion(screen, query->batch_uses));
371 struct zink_query_buffer *qbo, *next;
372
373 util_dynarray_foreach(&query->starts, struct zink_query_start, start) {
374 for (unsigned i = 0; i < PIPE_MAX_VERTEX_STREAMS; i++) {
375 if (!start->vkq[i])
376 continue;
377 start->vkq[i]->refcount--;
378 if (start->vkq[i]->refcount == 0)
379 FREE(start->vkq[i]);
380 }
381 }
382
383 util_dynarray_fini(&query->starts);
384 LIST_FOR_EACH_ENTRY_SAFE(qbo, next, &query->buffers, list) {
385 for (unsigned i = 0; i < ARRAY_SIZE(qbo->buffers); i++)
386 pipe_resource_reference(&qbo->buffers[i], NULL);
387 FREE(qbo);
388 }
389 pipe_resource_reference((struct pipe_resource**)&query->predicate, NULL);
390 FREE(query);
391 }
392
393 static void
reset_qbo(struct zink_query * q)394 reset_qbo(struct zink_query *q)
395 {
396 q->curr_qbo = list_first_entry(&q->buffers, struct zink_query_buffer, list);
397 q->curr_qbo->num_results = 0;
398 }
399
400 static void
query_pool_get_range(struct zink_context * ctx,struct zink_query * q)401 query_pool_get_range(struct zink_context *ctx, struct zink_query *q)
402 {
403 bool is_timestamp = q->type == PIPE_QUERY_TIMESTAMP || q->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
404 struct zink_query_start *start;
405 int num_queries = get_num_queries(q);
406 if (!is_timestamp || get_num_starts(q) == 0) {
407 start = util_dynarray_grow(&q->starts, struct zink_query_start, 1);
408 memset(start, 0, sizeof(*start));
409 } else {
410 start = util_dynarray_top_ptr(&q->starts, struct zink_query_start);
411 }
412
413 for (unsigned i = 0; i < num_queries; i++) {
414 int pool_idx = q->pool[1] ? i : 0;
415 /* try and find the active query for this */
416 struct zink_vk_query *vkq;
417 int xfb_idx = num_queries == 4 ? i : q->index;
418 if ((q->vkqtype == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT ||
419 (pool_idx == 1)) && ctx->curr_xfb_queries[xfb_idx]) {
420 vkq = ctx->curr_xfb_queries[xfb_idx];
421 vkq->refcount++;
422 } else {
423 struct zink_query_pool *pool = q->pool[pool_idx];
424 vkq = CALLOC_STRUCT(zink_vk_query);
425
426 vkq->refcount = 1;
427 vkq->needs_reset = true;
428 vkq->pool = pool;
429 vkq->started = false;
430 vkq->query_id = pool->last_range;
431
432 pool->last_range++;
433 if (pool->last_range == NUM_QUERIES)
434 pool->last_range = 0;
435 }
436 if (start->vkq[i])
437 FREE(start->vkq[i]);
438 start->vkq[i] = vkq;
439 }
440 }
441
442 static struct pipe_query *
zink_create_query(struct pipe_context * pctx,unsigned query_type,unsigned index)443 zink_create_query(struct pipe_context *pctx,
444 unsigned query_type, unsigned index)
445 {
446 struct zink_screen *screen = zink_screen(pctx->screen);
447 struct zink_query *query = CALLOC_STRUCT(zink_query);
448
449 if (!query)
450 return NULL;
451 list_inithead(&query->buffers);
452
453 query->index = index;
454 query->type = query_type;
455 if (query->type == PIPE_QUERY_GPU_FINISHED)
456 return (struct pipe_query *)query;
457 query->vkqtype = convert_query_type(screen, query_type, &query->precise);
458 if (query->vkqtype == -1)
459 return NULL;
460
461 util_dynarray_init(&query->starts, NULL);
462
463 assert(!query->precise || query->vkqtype == VK_QUERY_TYPE_OCCLUSION);
464
465 /* use emulated path for drivers without full support */
466 if (query->vkqtype == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT && index &&
467 !screen->info.primgen_feats.primitivesGeneratedQueryWithNonZeroStreams)
468 query->vkqtype = VK_QUERY_TYPE_PIPELINE_STATISTICS;
469
470 VkQueryPipelineStatisticFlags pipeline_stats = 0;
471 if (query->vkqtype == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT) {
472 query->needs_rast_discard_workaround = !screen->info.primgen_feats.primitivesGeneratedQueryWithRasterizerDiscard;
473 } else if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED) {
474 pipeline_stats = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT |
475 VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT;
476 query->needs_rast_discard_workaround = true;
477 } else if (query_type == PIPE_QUERY_PIPELINE_STATISTICS_SINGLE)
478 pipeline_stats = pipeline_statistic_convert(index);
479
480 int num_pools = get_num_query_pools(query);
481 for (unsigned i = 0; i < num_pools; i++) {
482 VkQueryType vkqtype = query->vkqtype;
483 /* if xfb is active, we need to use an xfb query, otherwise we need pipeline statistics */
484 if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED && i == 1) {
485 vkqtype = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
486 pipeline_stats = 0;
487 }
488 query->pool[i] = find_or_allocate_qp(zink_context(pctx),
489 vkqtype,
490 pipeline_stats);
491 if (!query->pool[i])
492 goto fail;
493 }
494
495 if (!qbo_append(pctx->screen, query))
496 goto fail;
497 struct zink_batch *batch = &zink_context(pctx)->batch;
498 batch->has_work = true;
499 query->needs_reset = true;
500 if (query->type == PIPE_QUERY_TIMESTAMP) {
501 query->active = true;
502 /* defer pool reset until end_query since we're guaranteed to be threadsafe then */
503 reset_qbo(query);
504 }
505 return (struct pipe_query *)query;
506 fail:
507 destroy_query(screen, query);
508 return NULL;
509 }
510
511 static void
zink_destroy_query(struct pipe_context * pctx,struct pipe_query * q)512 zink_destroy_query(struct pipe_context *pctx,
513 struct pipe_query *q)
514 {
515 struct zink_screen *screen = zink_screen(pctx->screen);
516 struct zink_query *query = (struct zink_query *)q;
517
518 /* only destroy if this query isn't active on any batches,
519 * otherwise just mark dead and wait
520 */
521 if (query->batch_uses) {
522 p_atomic_set(&query->dead, true);
523 return;
524 }
525
526 destroy_query(screen, query);
527 }
528
529 void
zink_prune_query(struct zink_screen * screen,struct zink_batch_state * bs,struct zink_query * query)530 zink_prune_query(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_query *query)
531 {
532 if (!zink_batch_usage_matches(query->batch_uses, bs))
533 return;
534 query->batch_uses = NULL;
535 if (p_atomic_read(&query->dead))
536 destroy_query(screen, query);
537 }
538
539 static void
check_query_results(struct zink_query * query,union pipe_query_result * result,int num_starts,uint64_t * results,uint64_t * xfb_results)540 check_query_results(struct zink_query *query, union pipe_query_result *result,
541 int num_starts, uint64_t *results, uint64_t *xfb_results)
542 {
543 uint64_t last_val = 0;
544 int result_size = get_num_results(query);
545 int idx = 0;
546 util_dynarray_foreach(&query->starts, struct zink_query_start, start) {
547 unsigned i = idx * result_size;
548 idx++;
549 switch (query->type) {
550 case PIPE_QUERY_OCCLUSION_PREDICATE:
551 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
552 case PIPE_QUERY_GPU_FINISHED:
553 result->b |= results[i] != 0;
554 break;
555
556 case PIPE_QUERY_TIME_ELAPSED:
557 case PIPE_QUERY_TIMESTAMP:
558 /* the application can sum the differences between all N queries to determine the total execution time.
559 * - 17.5. Timestamp Queries
560 */
561 if (query->type != PIPE_QUERY_TIME_ELAPSED || i)
562 result->u64 += results[i] - last_val;
563 last_val = results[i];
564 break;
565 case PIPE_QUERY_OCCLUSION_COUNTER:
566 result->u64 += results[i];
567 break;
568 case PIPE_QUERY_PRIMITIVES_GENERATED:
569 if (query->vkqtype == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT)
570 result->u64 += results[i];
571 else if (start->have_xfb || query->index)
572 result->u64 += xfb_results[i + 1];
573 else
574 /* if a given draw had a geometry shader, we need to use the first result */
575 result->u64 += results[i + !start->have_gs];
576 break;
577 case PIPE_QUERY_PRIMITIVES_EMITTED:
578 /* A query pool created with this type will capture 2 integers -
579 * numPrimitivesWritten and numPrimitivesNeeded -
580 * for the specified vertex stream output from the last vertex processing stage.
581 * - from VK_EXT_transform_feedback spec
582 */
583 result->u64 += results[i];
584 break;
585 case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
586 case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
587 /* A query pool created with this type will capture 2 integers -
588 * numPrimitivesWritten and numPrimitivesNeeded -
589 * for the specified vertex stream output from the last vertex processing stage.
590 * - from VK_EXT_transform_feedback spec
591 */
592 if (start->have_xfb)
593 result->b |= results[i] != results[i + 1];
594 break;
595 case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
596 switch (query->index) {
597 case PIPE_STAT_QUERY_IA_VERTICES:
598 result->u64 += start->was_line_loop ? results[i] / 2 : results[i];
599 break;
600 default:
601 result->u64 += results[i];
602 break;
603 }
604 break;
605
606 default:
607 debug_printf("unhandled query type: %s\n",
608 util_str_query_type(query->type, true));
609 unreachable("unexpected query type");
610 }
611 }
612 }
613
614 static bool
get_query_result(struct pipe_context * pctx,struct pipe_query * q,bool wait,union pipe_query_result * result)615 get_query_result(struct pipe_context *pctx,
616 struct pipe_query *q,
617 bool wait,
618 union pipe_query_result *result)
619 {
620 struct zink_screen *screen = zink_screen(pctx->screen);
621 struct zink_query *query = (struct zink_query *)q;
622 unsigned flags = PIPE_MAP_READ;
623
624 if (!wait)
625 flags |= PIPE_MAP_DONTBLOCK;
626 if (query->base.flushed)
627 /* this is not a context-safe operation; ensure map doesn't use slab alloc */
628 flags |= PIPE_MAP_THREAD_SAFE;
629
630 util_query_clear_result(result, query->type);
631
632 int num_starts = get_num_starts(query);
633 int result_size = get_num_results(query) * sizeof(uint64_t);
634 int num_maps = get_num_queries(query);
635
636 struct zink_query_buffer *qbo;
637 struct pipe_transfer *xfer[PIPE_MAX_VERTEX_STREAMS] = { 0 };
638 LIST_FOR_EACH_ENTRY(qbo, &query->buffers, list) {
639 uint64_t *results[PIPE_MAX_VERTEX_STREAMS] = { NULL, NULL };
640 bool is_timestamp = query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
641 if (!qbo->num_results)
642 continue;
643
644 for (unsigned i = 0; i < num_maps; i++) {
645 results[i] = pipe_buffer_map_range(pctx, qbo->buffers[i], 0,
646 (is_timestamp ? 1 : qbo->num_results) * result_size, flags, &xfer[i]);
647 if (!results[i]) {
648 if (wait)
649 debug_printf("zink: qbo read failed!");
650 goto fail;
651 }
652 }
653 if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
654 for (unsigned i = 0; i < PIPE_MAX_VERTEX_STREAMS && !result->b; i++) {
655 check_query_results(query, result, num_starts, results[i], NULL);
656 }
657 } else
658 check_query_results(query, result, num_starts, results[0], results[1]);
659
660 for (unsigned i = 0 ; i < num_maps; i++)
661 pipe_buffer_unmap(pctx, xfer[i]);
662
663 /* if overflow is detected we can stop */
664 if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE && result->b)
665 break;
666 }
667
668 if (is_time_query(query))
669 timestamp_to_nanoseconds(screen, &result->u64);
670
671 return true;
672 fail:
673 for (unsigned i = 0 ; i < num_maps; i++)
674 if (xfer[i])
675 pipe_buffer_unmap(pctx, xfer[i]);
676 return false;
677 }
678
679 static void
force_cpu_read(struct zink_context * ctx,struct pipe_query * pquery,enum pipe_query_value_type result_type,struct pipe_resource * pres,unsigned offset)680 force_cpu_read(struct zink_context *ctx, struct pipe_query *pquery, enum pipe_query_value_type result_type, struct pipe_resource *pres, unsigned offset)
681 {
682 struct pipe_context *pctx = &ctx->base;
683 unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t);
684 struct zink_query *query = (struct zink_query*)pquery;
685 union pipe_query_result result;
686
687 if (query->needs_update)
688 update_qbo(ctx, query);
689
690 bool success = get_query_result(pctx, pquery, true, &result);
691 if (!success) {
692 debug_printf("zink: getting query result failed\n");
693 return;
694 }
695
696 if (result_type <= PIPE_QUERY_TYPE_U32) {
697 uint32_t u32;
698 uint32_t limit;
699 if (result_type == PIPE_QUERY_TYPE_I32)
700 limit = INT_MAX;
701 else
702 limit = UINT_MAX;
703 if (is_bool_query(query))
704 u32 = result.b;
705 else
706 u32 = MIN2(limit, result.u64);
707 tc_buffer_write(pctx, pres, offset, result_size, &u32);
708 } else {
709 uint64_t u64;
710 if (is_bool_query(query))
711 u64 = result.b;
712 else
713 u64 = result.u64;
714 tc_buffer_write(pctx, pres, offset, result_size, &u64);
715 }
716 }
717
718 static void
copy_pool_results_to_buffer(struct zink_context * ctx,struct zink_query * query,VkQueryPool pool,unsigned query_id,struct zink_resource * res,unsigned offset,int num_results,VkQueryResultFlags flags)719 copy_pool_results_to_buffer(struct zink_context *ctx, struct zink_query *query, VkQueryPool pool,
720 unsigned query_id, struct zink_resource *res, unsigned offset,
721 int num_results, VkQueryResultFlags flags)
722 {
723 struct zink_batch *batch = &ctx->batch;
724 unsigned type_size = (flags & VK_QUERY_RESULT_64_BIT) ? sizeof(uint64_t) : sizeof(uint32_t);
725 unsigned base_result_size = get_num_results(query) * type_size;
726 unsigned result_size = base_result_size * num_results;
727 if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)
728 result_size += type_size;
729 zink_batch_no_rp(ctx);
730 /* if it's a single query that doesn't need special handling, we can copy it and be done */
731 zink_batch_reference_resource_rw(batch, res, true);
732 zink_resource_buffer_barrier(ctx, res, VK_ACCESS_TRANSFER_WRITE_BIT, 0);
733 util_range_add(&res->base.b, &res->valid_buffer_range, offset, offset + result_size);
734 assert(query_id < NUM_QUERIES);
735 res->obj->unordered_read = res->obj->unordered_write = false;
736 VKCTX(CmdCopyQueryPoolResults)(batch->state->cmdbuf, pool, query_id, num_results, res->obj->buffer,
737 offset, base_result_size, flags);
738 }
739
740 static void
copy_results_to_buffer(struct zink_context * ctx,struct zink_query * query,struct zink_resource * res,unsigned offset,int num_results,VkQueryResultFlags flags)741 copy_results_to_buffer(struct zink_context *ctx, struct zink_query *query, struct zink_resource *res, unsigned offset, int num_results, VkQueryResultFlags flags)
742 {
743 struct zink_query_start *start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
744 copy_pool_results_to_buffer(ctx, query, start->vkq[0]->pool->query_pool, start->vkq[0]->query_id, res, offset, num_results, flags);
745 }
746
747
748 static void
reset_query_range(struct zink_context * ctx,struct zink_query * q)749 reset_query_range(struct zink_context *ctx, struct zink_query *q)
750 {
751 int num_queries = get_num_queries(q);
752 zink_batch_no_rp(ctx);
753 struct zink_query_start *start = util_dynarray_top_ptr(&q->starts, struct zink_query_start);
754 for (unsigned i = 0; i < num_queries; i++) {
755 reset_vk_query_pool(ctx, start->vkq[i]);
756 }
757 }
758
759 static void
reset_qbos(struct zink_context * ctx,struct zink_query * q)760 reset_qbos(struct zink_context *ctx, struct zink_query *q)
761 {
762 if (q->needs_update)
763 update_qbo(ctx, q);
764
765 q->needs_reset = false;
766 /* create new qbo for non-timestamp queries:
767 * timestamp queries should never need more than 2 entries in the qbo
768 */
769 if (q->type == PIPE_QUERY_TIMESTAMP)
770 return;
771 if (qbo_append(ctx->base.screen, q))
772 reset_qbo(q);
773 else
774 debug_printf("zink: qbo alloc failed on reset!");
775 }
776
777 static inline unsigned
get_buffer_offset(struct zink_query * q)778 get_buffer_offset(struct zink_query *q)
779 {
780 return (get_num_starts(q) - q->last_start_idx - 1) * get_num_results(q) * sizeof(uint64_t);
781 }
782
783 static void
update_qbo(struct zink_context * ctx,struct zink_query * q)784 update_qbo(struct zink_context *ctx, struct zink_query *q)
785 {
786 struct zink_query_buffer *qbo = q->curr_qbo;
787 struct zink_query_start *start = util_dynarray_top_ptr(&q->starts, struct zink_query_start);
788 bool is_timestamp = q->type == PIPE_QUERY_TIMESTAMP || q->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
789 /* timestamp queries just write to offset 0 always */
790 int num_queries = get_num_queries(q);
791 for (unsigned i = 0; i < num_queries; i++) {
792 unsigned offset = is_timestamp ? 0 : get_buffer_offset(q);
793 copy_pool_results_to_buffer(ctx, q, start->vkq[i]->pool->query_pool, start->vkq[i]->query_id,
794 zink_resource(qbo->buffers[i]),
795 offset,
796 1,
797 /*
798 there is an implicit execution dependency from
799 each such query command to all query commands previously submitted to the same queue. There
800 is one significant exception to this; if the flags parameter of vkCmdCopyQueryPoolResults does not
801 include VK_QUERY_RESULT_WAIT_BIT, execution of vkCmdCopyQueryPoolResults may happen-before
802 the results of vkCmdEndQuery are available.
803
804 * - Chapter 18. Queries
805 */
806 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
807 }
808
809 if (!is_timestamp)
810 q->curr_qbo->num_results++;
811 else
812 q->curr_qbo->num_results = 1;
813 q->needs_update = false;
814 }
815
816 static void
begin_query(struct zink_context * ctx,struct zink_batch * batch,struct zink_query * q)817 begin_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
818 {
819 VkQueryControlFlags flags = 0;
820
821 update_query_id(ctx, q);
822 q->predicate_dirty = true;
823 if (q->needs_reset)
824 reset_qbos(ctx, q);
825 reset_query_range(ctx, q);
826 q->active = true;
827 batch->has_work = true;
828
829 struct zink_query_start *start = util_dynarray_top_ptr(&q->starts, struct zink_query_start);
830 if (q->type == PIPE_QUERY_TIME_ELAPSED) {
831 VKCTX(CmdWriteTimestamp)(batch->state->cmdbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, start->vkq[0]->pool->query_pool, start->vkq[0]->query_id);
832 update_qbo(ctx, q);
833 zink_batch_usage_set(&q->batch_uses, batch->state);
834 _mesa_set_add(batch->state->active_queries, q);
835 }
836 /* ignore the rest of begin_query for timestamps */
837 if (is_time_query(q))
838 return;
839 if (q->precise)
840 flags |= VK_QUERY_CONTROL_PRECISE_BIT;
841
842 if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
843 is_emulated_primgen(q) ||
844 q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
845 struct zink_vk_query *vkq = start->vkq[1] ? start->vkq[1] : start->vkq[0];
846 assert(!ctx->curr_xfb_queries[q->index] || ctx->curr_xfb_queries[q->index] == vkq);
847 ctx->curr_xfb_queries[q->index] = vkq;
848
849 begin_vk_query_indexed(ctx, vkq, q->index, flags);
850 } else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
851 for (unsigned i = 0; i < PIPE_MAX_VERTEX_STREAMS; i++) {
852 assert(!ctx->curr_xfb_queries[i] || ctx->curr_xfb_queries[i] == start->vkq[i]);
853 ctx->curr_xfb_queries[i] = start->vkq[i];
854
855 begin_vk_query_indexed(ctx, start->vkq[i], i, flags);
856 }
857 } else if (q->vkqtype == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT) {
858 begin_vk_query_indexed(ctx, start->vkq[0], q->index, flags);
859 }
860 if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT && q->vkqtype != VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT)
861 VKCTX(CmdBeginQuery)(batch->state->cmdbuf, start->vkq[0]->pool->query_pool, start->vkq[0]->query_id, flags);
862 if (q->type == PIPE_QUERY_PIPELINE_STATISTICS_SINGLE && q->index == PIPE_STAT_QUERY_IA_VERTICES) {
863 assert(!ctx->vertices_query);
864 ctx->vertices_query = q;
865 }
866 if (needs_stats_list(q))
867 list_addtail(&q->stats_list, &ctx->primitives_generated_queries);
868 zink_batch_usage_set(&q->batch_uses, batch->state);
869 _mesa_set_add(batch->state->active_queries, q);
870 if (q->needs_rast_discard_workaround) {
871 ctx->primitives_generated_active = true;
872 if (zink_set_rasterizer_discard(ctx, true))
873 zink_set_color_write_enables(ctx);
874 }
875 }
876
877 static bool
zink_begin_query(struct pipe_context * pctx,struct pipe_query * q)878 zink_begin_query(struct pipe_context *pctx,
879 struct pipe_query *q)
880 {
881 struct zink_query *query = (struct zink_query *)q;
882 struct zink_context *ctx = zink_context(pctx);
883 struct zink_batch *batch = &ctx->batch;
884
885 /* drop all past results */
886 reset_qbo(query);
887
888 util_dynarray_clear(&query->starts);
889
890 query->last_start_idx = get_num_starts(query);
891
892 /* A query must either begin and end inside the same subpass of a render pass
893 instance, or must both begin and end outside of a render pass instance
894 (i.e. contain entire render pass instances).
895 - 18.2. Query Operation
896
897 * tilers prefer out-of-renderpass queries for perf reasons, so force all queries
898 * out of renderpasses
899 */
900 zink_batch_no_rp(ctx);
901 begin_query(ctx, batch, query);
902
903 return true;
904 }
905
906 static void
update_query_id(struct zink_context * ctx,struct zink_query * q)907 update_query_id(struct zink_context *ctx, struct zink_query *q)
908 {
909 query_pool_get_range(ctx, q);
910 ctx->batch.has_work = true;
911 q->has_draws = false;
912 }
913
check_update(struct zink_context * ctx,struct zink_query * q)914 static void check_update(struct zink_context *ctx, struct zink_query *q)
915 {
916 if (ctx->batch.in_rp)
917 q->needs_update = true;
918 else
919 update_qbo(ctx, q);
920 }
921
922 static void
end_query(struct zink_context * ctx,struct zink_batch * batch,struct zink_query * q)923 end_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
924 {
925 ASSERTED struct zink_query_buffer *qbo = q->curr_qbo;
926 assert(qbo);
927 assert(!is_time_query(q));
928 q->active = false;
929 struct zink_query_start *start = util_dynarray_top_ptr(&q->starts, struct zink_query_start);
930
931 if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
932 is_emulated_primgen(q) ||
933 q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
934 struct zink_vk_query *vkq = start->vkq[1] ? start->vkq[1] : start->vkq[0];
935
936 end_vk_query_indexed(ctx, vkq, q->index);
937 ctx->curr_xfb_queries[q->index] = NULL;
938 }
939 else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
940 for (unsigned i = 0; i < PIPE_MAX_VERTEX_STREAMS; i++) {
941 end_vk_query_indexed(ctx, start->vkq[i], i);
942 ctx->curr_xfb_queries[i] = NULL;
943 }
944 } else if (q->vkqtype == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT) {
945 end_vk_query_indexed(ctx, start->vkq[0], q->index);
946 }
947 if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT &&
948 q->vkqtype != VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT && !is_time_query(q))
949 VKCTX(CmdEndQuery)(batch->state->cmdbuf, start->vkq[0]->pool->query_pool, start->vkq[0]->query_id);
950
951 if (q->type == PIPE_QUERY_PIPELINE_STATISTICS_SINGLE &&
952 q->index == PIPE_STAT_QUERY_IA_VERTICES)
953 ctx->vertices_query = NULL;
954
955 if (needs_stats_list(q))
956 list_delinit(&q->stats_list);
957
958 check_update(ctx, q);
959 if (q->needs_rast_discard_workaround) {
960 ctx->primitives_generated_active = false;
961 if (zink_set_rasterizer_discard(ctx, false))
962 zink_set_color_write_enables(ctx);
963 }
964 }
965
966 static bool
zink_end_query(struct pipe_context * pctx,struct pipe_query * q)967 zink_end_query(struct pipe_context *pctx,
968 struct pipe_query *q)
969 {
970 struct zink_context *ctx = zink_context(pctx);
971 struct zink_query *query = (struct zink_query *)q;
972 struct zink_batch *batch = &ctx->batch;
973
974 if (query->type == PIPE_QUERY_GPU_FINISHED) {
975 pctx->flush(pctx, &query->fence, PIPE_FLUSH_DEFERRED);
976 return true;
977 }
978
979 /* FIXME: this can be called from a thread, but it needs to write to the cmdbuf */
980 threaded_context_unwrap_sync(pctx);
981 zink_batch_no_rp(ctx);
982
983 if (needs_stats_list(query))
984 list_delinit(&query->stats_list);
985 if (is_time_query(query)) {
986 update_query_id(ctx, query);
987 if (query->needs_reset)
988 reset_qbos(ctx, query);
989 reset_query_range(ctx, query);
990 struct zink_query_start *start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
991 VKCTX(CmdWriteTimestamp)(batch->state->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
992 start->vkq[0]->pool->query_pool, start->vkq[0]->query_id);
993 zink_batch_usage_set(&query->batch_uses, batch->state);
994 _mesa_set_add(batch->state->active_queries, query);
995 check_update(ctx, query);
996 } else if (query->active)
997 end_query(ctx, batch, query);
998
999 return true;
1000 }
1001
1002 static bool
zink_get_query_result(struct pipe_context * pctx,struct pipe_query * q,bool wait,union pipe_query_result * result)1003 zink_get_query_result(struct pipe_context *pctx,
1004 struct pipe_query *q,
1005 bool wait,
1006 union pipe_query_result *result)
1007 {
1008 struct zink_query *query = (void*)q;
1009 struct zink_context *ctx = zink_context(pctx);
1010
1011 if (query->type == PIPE_QUERY_GPU_FINISHED) {
1012 struct pipe_screen *screen = pctx->screen;
1013
1014 result->b = screen->fence_finish(screen, query->base.flushed ? NULL : pctx,
1015 query->fence, wait ? PIPE_TIMEOUT_INFINITE : 0);
1016 return result->b;
1017 }
1018
1019 if (query->needs_update)
1020 update_qbo(ctx, query);
1021
1022 if (zink_batch_usage_is_unflushed(query->batch_uses)) {
1023 if (!threaded_query(q)->flushed)
1024 pctx->flush(pctx, NULL, 0);
1025 if (!wait)
1026 return false;
1027 }
1028
1029 return get_query_result(pctx, q, wait, result);
1030 }
1031
1032 static void
suspend_query(struct zink_context * ctx,struct zink_query * query)1033 suspend_query(struct zink_context *ctx, struct zink_query *query)
1034 {
1035 /* if a query isn't active here then we don't need to reactivate it on the next batch */
1036 if (query->active && !is_time_query(query))
1037 end_query(ctx, &ctx->batch, query);
1038 if (query->needs_update)
1039 update_qbo(ctx, query);
1040 }
1041
1042 void
zink_suspend_queries(struct zink_context * ctx,struct zink_batch * batch)1043 zink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch)
1044 {
1045 set_foreach(batch->state->active_queries, entry) {
1046 struct zink_query *query = (void*)entry->key;
1047 if (query->active && !is_time_query(query))
1048 /* the fence is going to steal the set off the batch, so we have to copy
1049 * the active queries onto a list
1050 */
1051 list_addtail(&query->active_list, &ctx->suspended_queries);
1052 suspend_query(ctx, query);
1053 }
1054 }
1055
1056 void
zink_resume_queries(struct zink_context * ctx,struct zink_batch * batch)1057 zink_resume_queries(struct zink_context *ctx, struct zink_batch *batch)
1058 {
1059 struct zink_query *query, *next;
1060 LIST_FOR_EACH_ENTRY_SAFE(query, next, &ctx->suspended_queries, active_list) {
1061 begin_query(ctx, batch, query);
1062 list_delinit(&query->active_list);
1063 }
1064 }
1065
1066 void
zink_query_update_gs_states(struct zink_context * ctx,bool was_line_loop)1067 zink_query_update_gs_states(struct zink_context *ctx, bool was_line_loop)
1068 {
1069 struct zink_query *query;
1070 bool suspendall = false;
1071 bool have_gs = !!ctx->gfx_stages[PIPE_SHADER_GEOMETRY];
1072 bool have_xfb = !!ctx->num_so_targets;
1073
1074 LIST_FOR_EACH_ENTRY(query, &ctx->primitives_generated_queries, stats_list) {
1075 struct zink_query_start *last_start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
1076 assert(query->active);
1077 if (query->has_draws) {
1078 if (last_start->have_gs != have_gs ||
1079 last_start->have_xfb != have_xfb) {
1080 suspendall = true;
1081 }
1082 }
1083 }
1084
1085 if (ctx->vertices_query) {
1086 query = ctx->vertices_query;
1087 struct zink_query_start *last_start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
1088 assert(query->active);
1089 if (last_start->was_line_loop != was_line_loop) {
1090 suspendall = true;
1091 }
1092 }
1093 if (suspendall) {
1094 zink_suspend_queries(ctx, &ctx->batch);
1095 zink_resume_queries(ctx, &ctx->batch);
1096 }
1097
1098 LIST_FOR_EACH_ENTRY(query, &ctx->primitives_generated_queries, stats_list) {
1099 struct zink_query_start *last_start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
1100 last_start->have_gs = have_gs;
1101 last_start->have_xfb = have_xfb;
1102 query->has_draws = true;
1103 }
1104 if (ctx->vertices_query) {
1105 query = ctx->vertices_query;
1106 struct zink_query_start *last_start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
1107 last_start->was_line_loop = was_line_loop;
1108 query->has_draws = true;
1109 }
1110 }
1111
1112 static void
zink_set_active_query_state(struct pipe_context * pctx,bool enable)1113 zink_set_active_query_state(struct pipe_context *pctx, bool enable)
1114 {
1115 struct zink_context *ctx = zink_context(pctx);
1116 ctx->queries_disabled = !enable;
1117
1118 struct zink_batch *batch = &ctx->batch;
1119 if (ctx->queries_disabled)
1120 zink_suspend_queries(ctx, batch);
1121 else
1122 zink_resume_queries(ctx, batch);
1123 }
1124
1125 void
zink_start_conditional_render(struct zink_context * ctx)1126 zink_start_conditional_render(struct zink_context *ctx)
1127 {
1128 if (unlikely(!zink_screen(ctx->base.screen)->info.have_EXT_conditional_rendering) || ctx->render_condition.active)
1129 return;
1130 struct zink_batch *batch = &ctx->batch;
1131 VkConditionalRenderingFlagsEXT begin_flags = 0;
1132 if (ctx->render_condition.inverted)
1133 begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
1134 VkConditionalRenderingBeginInfoEXT begin_info = {0};
1135 begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
1136 begin_info.buffer = ctx->render_condition.query->predicate->obj->buffer;
1137 begin_info.flags = begin_flags;
1138 ctx->render_condition.query->predicate->obj->unordered_read = false;
1139 VKCTX(CmdBeginConditionalRenderingEXT)(batch->state->cmdbuf, &begin_info);
1140 zink_batch_reference_resource_rw(batch, ctx->render_condition.query->predicate, false);
1141 ctx->render_condition.active = true;
1142 }
1143
1144 void
zink_stop_conditional_render(struct zink_context * ctx)1145 zink_stop_conditional_render(struct zink_context *ctx)
1146 {
1147 struct zink_batch *batch = &ctx->batch;
1148 zink_clear_apply_conditionals(ctx);
1149 if (unlikely(!zink_screen(ctx->base.screen)->info.have_EXT_conditional_rendering) || !ctx->render_condition.active)
1150 return;
1151 VKCTX(CmdEndConditionalRenderingEXT)(batch->state->cmdbuf);
1152 ctx->render_condition.active = false;
1153 }
1154
1155 bool
zink_check_conditional_render(struct zink_context * ctx)1156 zink_check_conditional_render(struct zink_context *ctx)
1157 {
1158 if (!ctx->render_condition_active)
1159 return true;
1160 assert(ctx->render_condition.query);
1161
1162 union pipe_query_result result;
1163 zink_get_query_result(&ctx->base, (struct pipe_query*)ctx->render_condition.query, true, &result);
1164 return is_bool_query(ctx->render_condition.query) ?
1165 ctx->render_condition.inverted != result.b :
1166 ctx->render_condition.inverted != !!result.u64;
1167 }
1168
1169 static void
zink_render_condition(struct pipe_context * pctx,struct pipe_query * pquery,bool condition,enum pipe_render_cond_flag mode)1170 zink_render_condition(struct pipe_context *pctx,
1171 struct pipe_query *pquery,
1172 bool condition,
1173 enum pipe_render_cond_flag mode)
1174 {
1175 struct zink_context *ctx = zink_context(pctx);
1176 struct zink_query *query = (struct zink_query *)pquery;
1177 zink_batch_no_rp(ctx);
1178 VkQueryResultFlagBits flags = 0;
1179
1180 if (query == NULL) {
1181 /* force conditional clears if they exist */
1182 if (ctx->clears_enabled && !ctx->batch.in_rp)
1183 zink_batch_rp(ctx);
1184 zink_stop_conditional_render(ctx);
1185 ctx->render_condition_active = false;
1186 ctx->render_condition.query = NULL;
1187 return;
1188 }
1189
1190 if (!query->predicate) {
1191 struct pipe_resource *pres;
1192
1193 /* need to create a vulkan buffer to copy the data into */
1194 pres = pipe_buffer_create(pctx->screen, PIPE_BIND_QUERY_BUFFER, PIPE_USAGE_DEFAULT, sizeof(uint64_t));
1195 if (!pres)
1196 return;
1197
1198 query->predicate = zink_resource(pres);
1199 }
1200 if (query->predicate_dirty) {
1201 struct zink_resource *res = query->predicate;
1202
1203 if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT)
1204 flags |= VK_QUERY_RESULT_WAIT_BIT;
1205
1206 flags |= VK_QUERY_RESULT_64_BIT;
1207 int num_results = get_num_starts(query);
1208 if (!is_emulated_primgen(query) &&
1209 !is_so_overflow_query(query)) {
1210 copy_results_to_buffer(ctx, query, res, 0, num_results, flags);
1211 } else {
1212 /* these need special handling */
1213 force_cpu_read(ctx, pquery, PIPE_QUERY_TYPE_U32, &res->base.b, 0);
1214 }
1215 zink_resource_buffer_barrier(ctx, res, VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT);
1216 query->predicate_dirty = false;
1217 }
1218 ctx->render_condition.inverted = condition;
1219 ctx->render_condition_active = true;
1220 ctx->render_condition.query = query;
1221 if (ctx->batch.in_rp)
1222 zink_start_conditional_render(ctx);
1223 }
1224
1225 static void
zink_get_query_result_resource(struct pipe_context * pctx,struct pipe_query * pquery,enum pipe_query_flags flags,enum pipe_query_value_type result_type,int index,struct pipe_resource * pres,unsigned offset)1226 zink_get_query_result_resource(struct pipe_context *pctx,
1227 struct pipe_query *pquery,
1228 enum pipe_query_flags flags,
1229 enum pipe_query_value_type result_type,
1230 int index,
1231 struct pipe_resource *pres,
1232 unsigned offset)
1233 {
1234 struct zink_context *ctx = zink_context(pctx);
1235 struct zink_screen *screen = zink_screen(pctx->screen);
1236 struct zink_query *query = (struct zink_query*)pquery;
1237 struct zink_resource *res = zink_resource(pres);
1238 unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t);
1239 VkQueryResultFlagBits size_flags = result_type <= PIPE_QUERY_TYPE_U32 ? 0 : VK_QUERY_RESULT_64_BIT;
1240 unsigned num_queries = (get_num_starts(query) - query->last_start_idx);
1241 struct zink_query_start *start = util_dynarray_top_ptr(&query->starts, struct zink_query_start);
1242 unsigned query_id = start->vkq[0]->query_id;
1243
1244 if (index == -1) {
1245 /* VK_QUERY_RESULT_WITH_AVAILABILITY_BIT will ALWAYS write some kind of result data
1246 * in addition to the availability result, which is a problem if we're just trying to get availability data
1247 *
1248 * if we know that there's no valid buffer data in the preceding buffer range, then we can just
1249 * stomp on it with a glorious queued buffer copy instead of forcing a stall to manually write to the
1250 * buffer
1251 */
1252
1253 VkQueryResultFlags flag = is_time_query(query) ? 0 : VK_QUERY_RESULT_PARTIAL_BIT;
1254 unsigned src_offset = result_size * get_num_results(query);
1255 if (zink_batch_usage_check_completion(ctx, query->batch_uses)) {
1256 uint64_t u64[4] = {0};
1257 VkResult result = VKCTX(GetQueryPoolResults)(screen->dev, start->vkq[0]->pool->query_pool, query_id, 1,
1258 sizeof(u64), u64, 0, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag);
1259 if (result == VK_SUCCESS) {
1260 tc_buffer_write(pctx, pres, offset, result_size, (unsigned char*)u64 + src_offset);
1261 return;
1262 } else {
1263 mesa_loge("ZINK: vkGetQueryPoolResults failed (%s)", vk_Result_to_str(result));
1264 }
1265 }
1266 struct pipe_resource *staging = pipe_buffer_create(pctx->screen, 0, PIPE_USAGE_STAGING, src_offset + result_size);
1267 copy_results_to_buffer(ctx, query, zink_resource(staging), 0, 1, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag);
1268 zink_copy_buffer(ctx, res, zink_resource(staging), offset, result_size * get_num_results(query), result_size);
1269 pipe_resource_reference(&staging, NULL);
1270 return;
1271 }
1272
1273 /*
1274 there is an implicit execution dependency from
1275 each such query command to all query commands previously submitted to the same queue. There
1276 is one significant exception to this; if the flags parameter of vkCmdCopyQueryPoolResults does not
1277 include VK_QUERY_RESULT_WAIT_BIT, execution of vkCmdCopyQueryPoolResults may happen-before
1278 the results of vkCmdEndQuery are available.
1279
1280 * - Chapter 18. Queries
1281 */
1282 size_flags |= VK_QUERY_RESULT_WAIT_BIT;
1283 if (!is_time_query(query) && !is_bool_query(query)) {
1284 if (num_queries == 1 && !is_emulated_primgen(query) &&
1285 query->type != PIPE_QUERY_PRIMITIVES_EMITTED &&
1286 !is_bool_query(query)) {
1287 if (size_flags == VK_QUERY_RESULT_64_BIT) {
1288 if (query->needs_update)
1289 update_qbo(ctx, query);
1290 /* internal qbo always writes 64bit value so we can just direct copy */
1291 zink_copy_buffer(ctx, res, zink_resource(query->curr_qbo->buffers[0]), offset,
1292 get_buffer_offset(query),
1293 result_size);
1294 } else
1295 /* have to do a new copy for 32bit */
1296 copy_results_to_buffer(ctx, query, res, offset, 1, size_flags);
1297 return;
1298 }
1299 }
1300
1301 /* TODO: use CS to aggregate results */
1302
1303 /* unfortunately, there's no way to accumulate results from multiple queries on the gpu without either
1304 * clobbering all but the last result or writing the results sequentially, so we have to manually write the result
1305 */
1306 force_cpu_read(ctx, pquery, result_type, pres, offset);
1307 }
1308
1309 uint64_t
zink_get_timestamp(struct pipe_screen * pscreen)1310 zink_get_timestamp(struct pipe_screen *pscreen)
1311 {
1312 struct zink_screen *screen = zink_screen(pscreen);
1313 uint64_t timestamp, deviation;
1314 if (screen->info.have_EXT_calibrated_timestamps) {
1315 VkCalibratedTimestampInfoEXT cti = {0};
1316 cti.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
1317 cti.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
1318 VkResult result = VKSCR(GetCalibratedTimestampsEXT)(screen->dev, 1, &cti, ×tamp, &deviation);
1319 if (result != VK_SUCCESS) {
1320 mesa_loge("ZINK: vkGetCalibratedTimestampsEXT failed (%s)", vk_Result_to_str(result));
1321 }
1322 } else {
1323 struct pipe_context *pctx = &screen->copy_context->base;
1324 struct pipe_query *pquery = pctx->create_query(pctx, PIPE_QUERY_TIMESTAMP, 0);
1325 if (!pquery)
1326 return 0;
1327 union pipe_query_result result = {0};
1328 pctx->begin_query(pctx, pquery);
1329 pctx->end_query(pctx, pquery);
1330 pctx->get_query_result(pctx, pquery, true, &result);
1331 pctx->destroy_query(pctx, pquery);
1332 timestamp = result.u64;
1333 }
1334 timestamp_to_nanoseconds(screen, ×tamp);
1335 return timestamp;
1336 }
1337
1338 void
zink_context_query_init(struct pipe_context * pctx)1339 zink_context_query_init(struct pipe_context *pctx)
1340 {
1341 struct zink_context *ctx = zink_context(pctx);
1342 list_inithead(&ctx->suspended_queries);
1343 list_inithead(&ctx->primitives_generated_queries);
1344
1345 pctx->create_query = zink_create_query;
1346 pctx->destroy_query = zink_destroy_query;
1347 pctx->begin_query = zink_begin_query;
1348 pctx->end_query = zink_end_query;
1349 pctx->get_query_result = zink_get_query_result;
1350 pctx->get_query_result_resource = zink_get_query_result_resource;
1351 pctx->set_active_query_state = zink_set_active_query_state;
1352 pctx->render_condition = zink_render_condition;
1353 }
1354