• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &timestamp, &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, &timestamp);
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