• 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_resource.h"
6 #include "zink_screen.h"
7 
8 #include "util/hash_table.h"
9 #include "util/set.h"
10 #include "util/u_dump.h"
11 #include "util/u_inlines.h"
12 #include "util/u_memory.h"
13 
14 #define NUM_QUERIES 50
15 
16 struct zink_query {
17    enum pipe_query_type type;
18 
19    VkQueryPool query_pool;
20    VkQueryPool xfb_query_pool;
21    unsigned curr_query, num_queries, last_start;
22 
23    VkQueryType vkqtype;
24    unsigned index;
25    bool use_64bit;
26    bool precise;
27    bool xfb_running;
28 
29    bool active; /* query is considered active by vk */
30    bool needs_reset; /* query is considered active by vk and cannot be destroyed */
31    bool dead; /* query should be destroyed when its fence finishes */
32 
33    unsigned fences;
34    struct list_head active_list;
35 
36    struct list_head stats_list; /* when active, statistics queries are added to ctx->primitives_generated_queries */
37    bool have_gs[NUM_QUERIES]; /* geometry shaders use GEOMETRY_SHADER_PRIMITIVES_BIT */
38    bool have_xfb[NUM_QUERIES]; /* xfb was active during this query */
39 
40    unsigned batch_id : 2; //batch that the query was started in
41 
42    union pipe_query_result accumulated_result;
43 };
44 
45 static void
timestamp_to_nanoseconds(struct zink_screen * screen,uint64_t * timestamp)46 timestamp_to_nanoseconds(struct zink_screen *screen, uint64_t *timestamp)
47 {
48    /* The number of valid bits in a timestamp value is determined by
49     * the VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is written.
50     * - 17.5. Timestamp Queries
51     */
52    *timestamp &= ((1ull << screen->timestamp_valid_bits) - 1);
53    /* The number of nanoseconds it takes for a timestamp value to be incremented by 1
54     * can be obtained from VkPhysicalDeviceLimits::timestampPeriod
55     * - 17.5. Timestamp Queries
56     */
57    *timestamp *= screen->info.props.limits.timestampPeriod;
58 }
59 
60 static VkQueryType
convert_query_type(unsigned query_type,bool * use_64bit,bool * precise)61 convert_query_type(unsigned query_type, bool *use_64bit, bool *precise)
62 {
63    *use_64bit = false;
64    *precise = false;
65    switch (query_type) {
66    case PIPE_QUERY_OCCLUSION_COUNTER:
67       *precise = true;
68       *use_64bit = true;
69       /* fallthrough */
70    case PIPE_QUERY_OCCLUSION_PREDICATE:
71    case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
72       return VK_QUERY_TYPE_OCCLUSION;
73    case PIPE_QUERY_TIME_ELAPSED:
74    case PIPE_QUERY_TIMESTAMP:
75       *use_64bit = true;
76       return VK_QUERY_TYPE_TIMESTAMP;
77    case PIPE_QUERY_PIPELINE_STATISTICS:
78    case PIPE_QUERY_PRIMITIVES_GENERATED:
79       return VK_QUERY_TYPE_PIPELINE_STATISTICS;
80    case PIPE_QUERY_PRIMITIVES_EMITTED:
81       *use_64bit = true;
82       return VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
83    default:
84       debug_printf("unknown query: %s\n",
85                    util_str_query_type(query_type, true));
86       unreachable("zink: unknown query type");
87    }
88 }
89 
90 static bool
is_time_query(struct zink_query * query)91 is_time_query(struct zink_query *query)
92 {
93    return query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIME_ELAPSED;
94 }
95 
96 static struct pipe_query *
zink_create_query(struct pipe_context * pctx,unsigned query_type,unsigned index)97 zink_create_query(struct pipe_context *pctx,
98                   unsigned query_type, unsigned index)
99 {
100    struct zink_screen *screen = zink_screen(pctx->screen);
101    struct zink_query *query = CALLOC_STRUCT(zink_query);
102    VkQueryPoolCreateInfo pool_create = {};
103 
104    if (!query)
105       return NULL;
106 
107    query->index = index;
108    query->type = query_type;
109    query->vkqtype = convert_query_type(query_type, &query->use_64bit, &query->precise);
110    if (query->vkqtype == -1)
111       return NULL;
112 
113    query->num_queries = NUM_QUERIES;
114    query->curr_query = 0;
115 
116    pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
117    pool_create.queryType = query->vkqtype;
118    pool_create.queryCount = query->num_queries;
119    if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED)
120      pool_create.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT |
121                                       VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT;
122 
123    VkResult status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->query_pool);
124    if (status != VK_SUCCESS) {
125       FREE(query);
126       return NULL;
127    }
128    if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED) {
129       /* if xfb is active, we need to use an xfb query, otherwise we need pipeline statistics */
130       pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
131       pool_create.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
132       pool_create.queryCount = query->num_queries;
133 
134       status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->xfb_query_pool);
135       if (status != VK_SUCCESS) {
136          vkDestroyQueryPool(screen->dev, query->query_pool, NULL);
137          FREE(query);
138          return NULL;
139       }
140    }
141    struct zink_batch *batch = zink_batch_no_rp(zink_context(pctx));
142    vkCmdResetQueryPool(batch->cmdbuf, query->query_pool, 0, query->num_queries);
143    if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED)
144       vkCmdResetQueryPool(batch->cmdbuf, query->xfb_query_pool, 0, query->num_queries);
145    if (query->type == PIPE_QUERY_TIMESTAMP)
146       query->active = true;
147    return (struct pipe_query *)query;
148 }
149 
150 static void
destroy_query(struct zink_screen * screen,struct zink_query * query)151 destroy_query(struct zink_screen *screen, struct zink_query *query)
152 {
153    assert(!p_atomic_read(&query->fences));
154    vkDestroyQueryPool(screen->dev, query->query_pool, NULL);
155    if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED)
156       vkDestroyQueryPool(screen->dev, query->xfb_query_pool, NULL);
157    FREE(query);
158 }
159 
160 static void
zink_destroy_query(struct pipe_context * pctx,struct pipe_query * q)161 zink_destroy_query(struct pipe_context *pctx,
162                    struct pipe_query *q)
163 {
164    struct zink_screen *screen = zink_screen(pctx->screen);
165    struct zink_query *query = (struct zink_query *)q;
166 
167    p_atomic_set(&query->dead, true);
168    if (p_atomic_read(&query->fences)) {
169       if (query->xfb_running)
170         zink_fence_wait(pctx);
171       return;
172    }
173 
174    destroy_query(screen, query);
175 }
176 
177 void
zink_prune_queries(struct zink_screen * screen,struct zink_fence * fence)178 zink_prune_queries(struct zink_screen *screen, struct zink_fence *fence)
179 {
180    set_foreach(fence->active_queries, entry) {
181       struct zink_query *query = (void*)entry->key;
182       if (!p_atomic_dec_return(&query->fences)) {
183          if (p_atomic_read(&query->dead))
184             destroy_query(screen, query);
185       }
186    }
187    _mesa_set_destroy(fence->active_queries, NULL);
188    fence->active_queries = NULL;
189 }
190 
191 static bool
get_query_result(struct pipe_context * pctx,struct pipe_query * q,bool wait,union pipe_query_result * result)192 get_query_result(struct pipe_context *pctx,
193                       struct pipe_query *q,
194                       bool wait,
195                       union pipe_query_result *result)
196 {
197    struct zink_screen *screen = zink_screen(pctx->screen);
198    struct zink_query *query = (struct zink_query *)q;
199    VkQueryResultFlagBits flags = 0;
200 
201    if (wait)
202       flags |= VK_QUERY_RESULT_WAIT_BIT;
203 
204    if (query->use_64bit)
205       flags |= VK_QUERY_RESULT_64_BIT;
206 
207    if (result != &query->accumulated_result) {
208       if (query->type == PIPE_QUERY_TIMESTAMP)
209          util_query_clear_result(result, query->type);
210       else {
211          memcpy(result, &query->accumulated_result, sizeof(query->accumulated_result));
212          util_query_clear_result(&query->accumulated_result, query->type);
213       }
214    } else
215       flags |= VK_QUERY_RESULT_PARTIAL_BIT;
216 
217    // union pipe_query_result results[NUM_QUERIES * 2];
218    /* xfb queries return 2 results */
219    uint64_t results[NUM_QUERIES * 2];
220    memset(results, 0, sizeof(results));
221    uint64_t xfb_results[NUM_QUERIES * 2];
222    memset(xfb_results, 0, sizeof(xfb_results));
223    int num_results = query->curr_query - query->last_start;
224    int result_size = 1;
225       /* these query types emit 2 values */
226    if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
227        query->type == PIPE_QUERY_PRIMITIVES_EMITTED)
228       result_size = 2;
229 
230    /* verify that we have the expected number of results pending */
231    assert(query->curr_query <= ARRAY_SIZE(results) / result_size);
232    VkResult status = vkGetQueryPoolResults(screen->dev, query->query_pool,
233                                            query->last_start, num_results,
234                                            sizeof(results),
235                                            results,
236                                            sizeof(uint64_t),
237                                            flags);
238    if (status != VK_SUCCESS)
239       return false;
240 
241    if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) {
242       status = vkGetQueryPoolResults(screen->dev, query->xfb_query_pool,
243                                               query->last_start, num_results,
244                                               sizeof(xfb_results),
245                                               xfb_results,
246                                               2 * sizeof(uint64_t),
247                                               flags | VK_QUERY_RESULT_64_BIT);
248       if (status != VK_SUCCESS)
249          return false;
250 
251    }
252 
253    uint64_t last_val = 0;
254    for (int i = 0; i < num_results * result_size; i += result_size) {
255       switch (query->type) {
256       case PIPE_QUERY_OCCLUSION_PREDICATE:
257       case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
258       case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
259       case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
260       case PIPE_QUERY_GPU_FINISHED:
261          result->b |= results[i] != 0;
262          break;
263 
264       case PIPE_QUERY_TIME_ELAPSED:
265       case PIPE_QUERY_TIMESTAMP:
266          /* the application can sum the differences between all N queries to determine the total execution time.
267           * - 17.5. Timestamp Queries
268           */
269          if (query->type != PIPE_QUERY_TIME_ELAPSED || i > 0)
270             result->u64 += results[i] - last_val;
271          last_val = results[i];
272          break;
273       case PIPE_QUERY_OCCLUSION_COUNTER:
274          result->u64 += results[i];
275          break;
276       case PIPE_QUERY_PRIMITIVES_GENERATED:
277          if (query->have_xfb[query->last_start + i / 2] || query->index)
278             result->u64 += xfb_results[i + 1];
279          else
280             /* if a given draw had a geometry shader, we need to use the second result */
281             result->u32 += ((uint32_t*)results)[i + query->have_gs[query->last_start + i / 2]];
282          break;
283       case PIPE_QUERY_PRIMITIVES_EMITTED:
284          /* A query pool created with this type will capture 2 integers -
285           * numPrimitivesWritten and numPrimitivesNeeded -
286           * for the specified vertex stream output from the last vertex processing stage.
287           * - from VK_EXT_transform_feedback spec
288           */
289          result->u64 += results[i];
290          break;
291 
292       default:
293          debug_printf("unhangled query type: %s\n",
294                       util_str_query_type(query->type, true));
295          unreachable("unexpected query type");
296       }
297    }
298 
299    if (is_time_query(query))
300       timestamp_to_nanoseconds(screen, &result->u64);
301 
302    return TRUE;
303 }
304 
305 static void
reset_pool(struct zink_context * ctx,struct zink_batch * batch,struct zink_query * q)306 reset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
307 {
308    /* This command must only be called outside of a render pass instance
309     *
310     * - vkCmdResetQueryPool spec
311     */
312    batch = zink_batch_no_rp(ctx);
313 
314    if (q->type != PIPE_QUERY_TIMESTAMP)
315       get_query_result(&ctx->base, (struct pipe_query*)q, false, &q->accumulated_result);
316    vkCmdResetQueryPool(batch->cmdbuf, q->query_pool, 0, q->num_queries);
317    if (q->type == PIPE_QUERY_PRIMITIVES_GENERATED)
318       vkCmdResetQueryPool(batch->cmdbuf, q->xfb_query_pool, 0, q->num_queries);
319    memset(q->have_gs, 0, sizeof(q->have_gs));
320    memset(q->have_xfb, 0, sizeof(q->have_xfb));
321    q->last_start = q->curr_query = 0;
322    q->needs_reset = false;
323 }
324 
325 static void
begin_query(struct zink_context * ctx,struct zink_batch * batch,struct zink_query * q)326 begin_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
327 {
328    VkQueryControlFlags flags = 0;
329 
330    if (q->needs_reset)
331       reset_pool(ctx, batch, q);
332    assert(q->curr_query < q->num_queries);
333    q->active = true;
334    if (q->type == PIPE_QUERY_TIME_ELAPSED)
335       vkCmdWriteTimestamp(batch->cmdbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, q->query_pool, q->curr_query++);
336    /* ignore the rest of begin_query for timestamps */
337    if (is_time_query(q))
338       return;
339    if (q->precise)
340       flags |= VK_QUERY_CONTROL_PRECISE_BIT;
341    if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED || q->type == PIPE_QUERY_PRIMITIVES_GENERATED) {
342       zink_screen(ctx->base.screen)->vk_CmdBeginQueryIndexedEXT(batch->cmdbuf,
343                                                                 q->xfb_query_pool ? q->xfb_query_pool : q->query_pool,
344                                                                 q->curr_query,
345                                                                 flags,
346                                                                 q->index);
347       q->xfb_running = true;
348    }
349    if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
350       vkCmdBeginQuery(batch->cmdbuf, q->query_pool, q->curr_query, flags);
351    if (!batch->active_queries)
352       batch->active_queries = _mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
353    assert(batch->active_queries);
354    if (q->type == PIPE_QUERY_PRIMITIVES_GENERATED)
355       list_addtail(&q->stats_list, &ctx->primitives_generated_queries);
356    p_atomic_inc(&q->fences);
357    q->batch_id = batch->batch_id;
358    _mesa_set_add(batch->active_queries, q);
359 }
360 
361 static bool
zink_begin_query(struct pipe_context * pctx,struct pipe_query * q)362 zink_begin_query(struct pipe_context *pctx,
363                  struct pipe_query *q)
364 {
365    struct zink_query *query = (struct zink_query *)q;
366    struct zink_context *ctx = zink_context(pctx);
367    struct zink_batch *batch = zink_curr_batch(ctx);
368 
369    query->last_start = query->curr_query;
370 
371    util_query_clear_result(&query->accumulated_result, query->type);
372 
373    begin_query(ctx, batch, query);
374 
375    return true;
376 }
377 
378 static void
end_query(struct zink_context * ctx,struct zink_batch * batch,struct zink_query * q)379 end_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
380 {
381    struct zink_screen *screen = zink_screen(ctx->base.screen);
382    q->active = q->type == PIPE_QUERY_TIMESTAMP;
383    if (is_time_query(q)) {
384       vkCmdWriteTimestamp(batch->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
385                           q->query_pool, q->curr_query);
386       q->batch_id = batch->batch_id;
387    } else if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED || q->type == PIPE_QUERY_PRIMITIVES_GENERATED)
388       screen->vk_CmdEndQueryIndexedEXT(batch->cmdbuf, q->xfb_query_pool ? q->xfb_query_pool : q->query_pool, q->curr_query, q->index);
389    if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT && !is_time_query(q))
390       vkCmdEndQuery(batch->cmdbuf, q->query_pool, q->curr_query);
391    if (q->type == PIPE_QUERY_PRIMITIVES_GENERATED)
392       list_delinit(&q->stats_list);
393    if (++q->curr_query == q->num_queries) {
394       /* always reset on start; this ensures we can actually submit the batch that the current query is on */
395       q->needs_reset = true;
396    }
397 }
398 
399 static bool
zink_end_query(struct pipe_context * pctx,struct pipe_query * q)400 zink_end_query(struct pipe_context *pctx,
401                struct pipe_query *q)
402 {
403    struct zink_context *ctx = zink_context(pctx);
404    struct zink_query *query = (struct zink_query *)q;
405    struct zink_batch *batch = zink_curr_batch(ctx);
406 
407    if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED)
408       list_delinit(&query->stats_list);
409    if (query->active)
410       end_query(ctx, batch, query);
411 
412    return true;
413 }
414 
415 static bool
zink_get_query_result(struct pipe_context * pctx,struct pipe_query * q,bool wait,union pipe_query_result * result)416 zink_get_query_result(struct pipe_context *pctx,
417                       struct pipe_query *q,
418                       bool wait,
419                       union pipe_query_result *result)
420 {
421    if (wait) {
422       zink_fence_wait(pctx);
423    } else
424       pctx->flush(pctx, NULL, 0);
425    return get_query_result(pctx, q, wait, result);
426 }
427 
428 void
zink_suspend_queries(struct zink_context * ctx,struct zink_batch * batch)429 zink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch)
430 {
431    if (!batch->active_queries)
432       return;
433    set_foreach(batch->active_queries, entry) {
434       struct zink_query *query = (void*)entry->key;
435       /* if a query isn't active here then we don't need to reactivate it on the next batch */
436       if (query->active) {
437          end_query(ctx, batch, query);
438          /* the fence is going to steal the set off the batch, so we have to copy
439           * the active queries onto a list
440           */
441          list_addtail(&query->active_list, &ctx->suspended_queries);
442       }
443    }
444 }
445 
446 void
zink_resume_queries(struct zink_context * ctx,struct zink_batch * batch)447 zink_resume_queries(struct zink_context *ctx, struct zink_batch *batch)
448 {
449    struct zink_query *query, *next;
450    LIST_FOR_EACH_ENTRY_SAFE(query, next, &ctx->suspended_queries, active_list) {
451       begin_query(ctx, batch, query);
452       list_delinit(&query->active_list);
453    }
454 }
455 
456 void
zink_query_update_gs_states(struct zink_context * ctx)457 zink_query_update_gs_states(struct zink_context *ctx)
458 {
459    struct zink_query *query;
460    LIST_FOR_EACH_ENTRY(query, &ctx->primitives_generated_queries, stats_list) {
461       assert(query->curr_query < ARRAY_SIZE(query->have_gs));
462       assert(query->active);
463       query->have_gs[query->curr_query] = !!ctx->gfx_stages[PIPE_SHADER_GEOMETRY];
464       query->have_xfb[query->curr_query] = !!ctx->num_so_targets;
465    }
466 }
467 
468 static void
zink_set_active_query_state(struct pipe_context * pctx,bool enable)469 zink_set_active_query_state(struct pipe_context *pctx, bool enable)
470 {
471    struct zink_context *ctx = zink_context(pctx);
472    ctx->queries_disabled = !enable;
473 
474    struct zink_batch *batch = zink_curr_batch(ctx);
475    if (ctx->queries_disabled)
476       zink_suspend_queries(ctx, batch);
477    else
478       zink_resume_queries(ctx, batch);
479 }
480 
481 static void
zink_render_condition(struct pipe_context * pctx,struct pipe_query * pquery,bool condition,enum pipe_render_cond_flag mode)482 zink_render_condition(struct pipe_context *pctx,
483                       struct pipe_query *pquery,
484                       bool condition,
485                       enum pipe_render_cond_flag mode)
486 {
487    struct zink_context *ctx = zink_context(pctx);
488    struct zink_screen *screen = zink_screen(pctx->screen);
489    struct zink_query *query = (struct zink_query *)pquery;
490    struct zink_batch *batch = zink_batch_no_rp(ctx);
491    VkQueryResultFlagBits flags = 0;
492 
493    if (query == NULL) {
494       screen->vk_CmdEndConditionalRenderingEXT(batch->cmdbuf);
495       ctx->render_condition_active = false;
496       return;
497    }
498 
499    struct pipe_resource *pres;
500    struct zink_resource *res;
501    struct pipe_resource templ = {};
502    templ.width0 = 8;
503    templ.height0 = 1;
504    templ.depth0 = 1;
505    templ.format = PIPE_FORMAT_R8_UINT;
506    templ.target = PIPE_BUFFER;
507 
508    /* need to create a vulkan buffer to copy the data into */
509    pres = pctx->screen->resource_create(pctx->screen, &templ);
510    if (!pres)
511       return;
512 
513    res = (struct zink_resource *)pres;
514 
515    if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT)
516       flags |= VK_QUERY_RESULT_WAIT_BIT;
517 
518    if (query->use_64bit)
519       flags |= VK_QUERY_RESULT_64_BIT;
520    int num_results = query->curr_query - query->last_start;
521    vkCmdCopyQueryPoolResults(batch->cmdbuf, query->query_pool, query->last_start, num_results,
522                              res->buffer, 0, 0, flags);
523 
524    VkConditionalRenderingFlagsEXT begin_flags = 0;
525    if (condition)
526       begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
527    VkConditionalRenderingBeginInfoEXT begin_info = {};
528    begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
529    begin_info.buffer = res->buffer;
530    begin_info.flags = begin_flags;
531    screen->vk_CmdBeginConditionalRenderingEXT(batch->cmdbuf, &begin_info);
532    ctx->render_condition_active = true;
533 
534    zink_batch_reference_resource_rw(batch, res, true);
535 
536    pipe_resource_reference(&pres, NULL);
537 }
538 
539 static uint64_t
zink_get_timestamp(struct pipe_context * pctx)540 zink_get_timestamp(struct pipe_context *pctx)
541 {
542    struct zink_screen *screen = zink_screen(pctx->screen);
543    uint64_t timestamp, deviation;
544    assert(screen->info.have_EXT_calibrated_timestamps);
545    VkCalibratedTimestampInfoEXT cti = {};
546    cti.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
547    cti.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
548    screen->vk_GetCalibratedTimestampsEXT(screen->dev, 1, &cti, &timestamp, &deviation);
549    timestamp_to_nanoseconds(screen, &timestamp);
550    return timestamp;
551 }
552 
553 void
zink_context_query_init(struct pipe_context * pctx)554 zink_context_query_init(struct pipe_context *pctx)
555 {
556    struct zink_context *ctx = zink_context(pctx);
557    list_inithead(&ctx->suspended_queries);
558    list_inithead(&ctx->primitives_generated_queries);
559 
560    pctx->create_query = zink_create_query;
561    pctx->destroy_query = zink_destroy_query;
562    pctx->begin_query = zink_begin_query;
563    pctx->end_query = zink_end_query;
564    pctx->get_query_result = zink_get_query_result;
565    pctx->set_active_query_state = zink_set_active_query_state;
566    pctx->render_condition = zink_render_condition;
567    pctx->get_timestamp = zink_get_timestamp;
568 }
569