17ec681f3Smrg#include "zink_query.h"
27ec681f3Smrg
37ec681f3Smrg#include "zink_context.h"
47ec681f3Smrg#include "zink_fence.h"
57ec681f3Smrg#include "zink_resource.h"
67ec681f3Smrg#include "zink_screen.h"
77ec681f3Smrg
87ec681f3Smrg#include "util/hash_table.h"
97ec681f3Smrg#include "util/set.h"
107ec681f3Smrg#include "util/u_dump.h"
117ec681f3Smrg#include "util/u_inlines.h"
127ec681f3Smrg#include "util/u_memory.h"
137ec681f3Smrg
147ec681f3Smrg#if defined(PIPE_ARCH_X86_64) || defined(PIPE_ARCH_PPC_64) || defined(PIPE_ARCH_AARCH64) || defined(PIPE_ARCH_MIPS64)
157ec681f3Smrg#define NUM_QUERIES 5000
167ec681f3Smrg#else
177ec681f3Smrg#define NUM_QUERIES 500
187ec681f3Smrg#endif
197ec681f3Smrg
207ec681f3Smrgstruct zink_query_buffer {
217ec681f3Smrg   struct list_head list;
227ec681f3Smrg   unsigned num_results;
237ec681f3Smrg   struct pipe_resource *buffer;
247ec681f3Smrg   struct pipe_resource *xfb_buffers[PIPE_MAX_VERTEX_STREAMS - 1];
257ec681f3Smrg};
267ec681f3Smrg
277ec681f3Smrgstruct zink_query {
287ec681f3Smrg   struct threaded_query base;
297ec681f3Smrg   enum pipe_query_type type;
307ec681f3Smrg
317ec681f3Smrg   VkQueryPool query_pool;
327ec681f3Smrg   VkQueryPool xfb_query_pool[PIPE_MAX_VERTEX_STREAMS - 1]; //stream 0 is in the base pool
337ec681f3Smrg   unsigned curr_query, last_start;
347ec681f3Smrg
357ec681f3Smrg   VkQueryType vkqtype;
367ec681f3Smrg   unsigned index;
377ec681f3Smrg   bool precise;
387ec681f3Smrg   bool xfb_running;
397ec681f3Smrg   bool xfb_overflow;
407ec681f3Smrg
417ec681f3Smrg   bool active; /* query is considered active by vk */
427ec681f3Smrg   bool needs_reset; /* query is considered active by vk and cannot be destroyed */
437ec681f3Smrg   bool dead; /* query should be destroyed when its fence finishes */
447ec681f3Smrg   bool needs_update; /* query needs to update its qbos */
457ec681f3Smrg
467ec681f3Smrg   struct list_head active_list;
477ec681f3Smrg
487ec681f3Smrg   struct list_head stats_list; /* when active, statistics queries are added to ctx->primitives_generated_queries */
497ec681f3Smrg   bool have_gs[NUM_QUERIES]; /* geometry shaders use GEOMETRY_SHADER_PRIMITIVES_BIT */
507ec681f3Smrg   bool have_xfb[NUM_QUERIES]; /* xfb was active during this query */
517ec681f3Smrg
527ec681f3Smrg   struct zink_batch_usage *batch_id; //batch that the query was started in
537ec681f3Smrg
547ec681f3Smrg   struct list_head buffers;
557ec681f3Smrg   union {
567ec681f3Smrg      struct zink_query_buffer *curr_qbo;
577ec681f3Smrg      struct pipe_fence_handle *fence; //PIPE_QUERY_GPU_FINISHED
587ec681f3Smrg   };
597ec681f3Smrg
607ec681f3Smrg   struct zink_resource *predicate;
617ec681f3Smrg   bool predicate_dirty;
627ec681f3Smrg};
637ec681f3Smrg
647ec681f3Smrgstatic void
657ec681f3Smrgupdate_qbo(struct zink_context *ctx, struct zink_query *q);
667ec681f3Smrgstatic void
677ec681f3Smrgreset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q);
687ec681f3Smrg
697ec681f3Smrgstatic inline unsigned
707ec681f3Smrgget_num_results(enum pipe_query_type query_type)
717ec681f3Smrg{
727ec681f3Smrg   switch (query_type) {
737ec681f3Smrg   case PIPE_QUERY_OCCLUSION_COUNTER:
747ec681f3Smrg   case PIPE_QUERY_OCCLUSION_PREDICATE:
757ec681f3Smrg   case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
767ec681f3Smrg   case PIPE_QUERY_TIME_ELAPSED:
777ec681f3Smrg   case PIPE_QUERY_TIMESTAMP:
787ec681f3Smrg   case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
797ec681f3Smrg      return 1;
807ec681f3Smrg   case PIPE_QUERY_PRIMITIVES_GENERATED:
817ec681f3Smrg   case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
827ec681f3Smrg   case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
837ec681f3Smrg   case PIPE_QUERY_PRIMITIVES_EMITTED:
847ec681f3Smrg      return 2;
857ec681f3Smrg   default:
867ec681f3Smrg      debug_printf("unknown query: %s\n",
877ec681f3Smrg                   util_str_query_type(query_type, true));
887ec681f3Smrg      unreachable("zink: unknown query type");
897ec681f3Smrg   }
907ec681f3Smrg}
917ec681f3Smrg
927ec681f3Smrgstatic VkQueryPipelineStatisticFlags
937ec681f3Smrgpipeline_statistic_convert(enum pipe_statistics_query_index idx)
947ec681f3Smrg{
957ec681f3Smrg   unsigned map[] = {
967ec681f3Smrg      [PIPE_STAT_QUERY_IA_VERTICES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT,
977ec681f3Smrg      [PIPE_STAT_QUERY_IA_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT,
987ec681f3Smrg      [PIPE_STAT_QUERY_VS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT,
997ec681f3Smrg      [PIPE_STAT_QUERY_GS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT,
1007ec681f3Smrg      [PIPE_STAT_QUERY_GS_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT,
1017ec681f3Smrg      [PIPE_STAT_QUERY_C_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT,
1027ec681f3Smrg      [PIPE_STAT_QUERY_C_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT,
1037ec681f3Smrg      [PIPE_STAT_QUERY_PS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT,
1047ec681f3Smrg      [PIPE_STAT_QUERY_HS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT,
1057ec681f3Smrg      [PIPE_STAT_QUERY_DS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT,
1067ec681f3Smrg      [PIPE_STAT_QUERY_CS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT
1077ec681f3Smrg   };
1087ec681f3Smrg   assert(idx < ARRAY_SIZE(map));
1097ec681f3Smrg   return map[idx];
1107ec681f3Smrg}
1117ec681f3Smrg
1127ec681f3Smrgstatic void
1137ec681f3Smrgtimestamp_to_nanoseconds(struct zink_screen *screen, uint64_t *timestamp)
1147ec681f3Smrg{
1157ec681f3Smrg   /* The number of valid bits in a timestamp value is determined by
1167ec681f3Smrg    * the VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is written.
1177ec681f3Smrg    * - 17.5. Timestamp Queries
1187ec681f3Smrg    */
1197ec681f3Smrg   if (screen->timestamp_valid_bits < 64)
1207ec681f3Smrg      *timestamp &= (1ull << screen->timestamp_valid_bits) - 1;
1217ec681f3Smrg
1227ec681f3Smrg   /* The number of nanoseconds it takes for a timestamp value to be incremented by 1
1237ec681f3Smrg    * can be obtained from VkPhysicalDeviceLimits::timestampPeriod
1247ec681f3Smrg    * - 17.5. Timestamp Queries
1257ec681f3Smrg    */
1267ec681f3Smrg   *timestamp *= screen->info.props.limits.timestampPeriod;
1277ec681f3Smrg}
1287ec681f3Smrg
1297ec681f3Smrgstatic VkQueryType
1307ec681f3Smrgconvert_query_type(unsigned query_type, bool *precise)
1317ec681f3Smrg{
1327ec681f3Smrg   *precise = false;
1337ec681f3Smrg   switch (query_type) {
1347ec681f3Smrg   case PIPE_QUERY_OCCLUSION_COUNTER:
1357ec681f3Smrg      *precise = true;
1367ec681f3Smrg      FALLTHROUGH;
1377ec681f3Smrg   case PIPE_QUERY_OCCLUSION_PREDICATE:
1387ec681f3Smrg   case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
1397ec681f3Smrg      return VK_QUERY_TYPE_OCCLUSION;
1407ec681f3Smrg   case PIPE_QUERY_TIME_ELAPSED:
1417ec681f3Smrg   case PIPE_QUERY_TIMESTAMP:
1427ec681f3Smrg      return VK_QUERY_TYPE_TIMESTAMP;
1437ec681f3Smrg   case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
1447ec681f3Smrg   case PIPE_QUERY_PRIMITIVES_GENERATED:
1457ec681f3Smrg      return VK_QUERY_TYPE_PIPELINE_STATISTICS;
1467ec681f3Smrg   case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
1477ec681f3Smrg   case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
1487ec681f3Smrg   case PIPE_QUERY_PRIMITIVES_EMITTED:
1497ec681f3Smrg      return VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
1507ec681f3Smrg   default:
1517ec681f3Smrg      debug_printf("unknown query: %s\n",
1527ec681f3Smrg                   util_str_query_type(query_type, true));
1537ec681f3Smrg      unreachable("zink: unknown query type");
1547ec681f3Smrg   }
1557ec681f3Smrg}
1567ec681f3Smrg
1577ec681f3Smrgstatic bool
1587ec681f3Smrgneeds_stats_list(struct zink_query *query)
1597ec681f3Smrg{
1607ec681f3Smrg   return query->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
1617ec681f3Smrg          query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE ||
1627ec681f3Smrg          query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE;
1637ec681f3Smrg}
1647ec681f3Smrg
1657ec681f3Smrgstatic bool
1667ec681f3Smrgis_time_query(struct zink_query *query)
1677ec681f3Smrg{
1687ec681f3Smrg   return query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIME_ELAPSED;
1697ec681f3Smrg}
1707ec681f3Smrg
1717ec681f3Smrgstatic bool
1727ec681f3Smrgis_so_overflow_query(struct zink_query *query)
1737ec681f3Smrg{
1747ec681f3Smrg   return query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE || query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE;
1757ec681f3Smrg}
1767ec681f3Smrg
1777ec681f3Smrgstatic bool
1787ec681f3Smrgis_bool_query(struct zink_query *query)
1797ec681f3Smrg{
1807ec681f3Smrg   return is_so_overflow_query(query) ||
1817ec681f3Smrg          query->type == PIPE_QUERY_OCCLUSION_PREDICATE ||
1827ec681f3Smrg          query->type == PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE ||
1837ec681f3Smrg          query->type == PIPE_QUERY_GPU_FINISHED;
1847ec681f3Smrg}
1857ec681f3Smrg
1867ec681f3Smrgstatic bool
1877ec681f3Smrgqbo_append(struct pipe_screen *screen, struct zink_query *query)
1887ec681f3Smrg{
1897ec681f3Smrg   if (query->curr_qbo && query->curr_qbo->list.next)
1907ec681f3Smrg      return true;
1917ec681f3Smrg   struct zink_query_buffer *qbo = CALLOC_STRUCT(zink_query_buffer);
1927ec681f3Smrg   if (!qbo)
1937ec681f3Smrg      return false;
1947ec681f3Smrg   qbo->buffer = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
1957ec681f3Smrg                                  PIPE_USAGE_STAGING,
1967ec681f3Smrg                                  /* this is the maximum possible size of the results in a given buffer */
1977ec681f3Smrg                                  NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t));
1987ec681f3Smrg   if (!qbo->buffer)
1997ec681f3Smrg      goto fail;
2007ec681f3Smrg   if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) {
2017ec681f3Smrg      /* need separate xfb buffer */
2027ec681f3Smrg      qbo->xfb_buffers[0] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
2037ec681f3Smrg                                     PIPE_USAGE_STAGING,
2047ec681f3Smrg                                     /* this is the maximum possible size of the results in a given buffer */
2057ec681f3Smrg                                     NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t));
2067ec681f3Smrg      if (!qbo->xfb_buffers[0])
2077ec681f3Smrg         goto fail;
2087ec681f3Smrg   } else if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
2097ec681f3Smrg      /* need to monitor all xfb streams */
2107ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++) {
2117ec681f3Smrg         /* need separate xfb buffer */
2127ec681f3Smrg         qbo->xfb_buffers[i] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
2137ec681f3Smrg                                        PIPE_USAGE_STAGING,
2147ec681f3Smrg                                        /* this is the maximum possible size of the results in a given buffer */
2157ec681f3Smrg                                        NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t));
2167ec681f3Smrg         if (!qbo->xfb_buffers[i])
2177ec681f3Smrg            goto fail;
2187ec681f3Smrg      }
2197ec681f3Smrg   }
2207ec681f3Smrg   list_addtail(&qbo->list, &query->buffers);
2217ec681f3Smrg
2227ec681f3Smrg   return true;
2237ec681f3Smrgfail:
2247ec681f3Smrg   pipe_resource_reference(&qbo->buffer, NULL);
2257ec681f3Smrg   for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++)
2267ec681f3Smrg      pipe_resource_reference(&qbo->xfb_buffers[i], NULL);
2277ec681f3Smrg   FREE(qbo);
2287ec681f3Smrg   return false;
2297ec681f3Smrg}
2307ec681f3Smrg
2317ec681f3Smrgstatic void
2327ec681f3Smrgdestroy_query(struct zink_screen *screen, struct zink_query *query)
2337ec681f3Smrg{
2347ec681f3Smrg   assert(zink_screen_usage_check_completion(screen, query->batch_id));
2357ec681f3Smrg   if (query->query_pool)
2367ec681f3Smrg      VKSCR(DestroyQueryPool)(screen->dev, query->query_pool, NULL);
2377ec681f3Smrg   struct zink_query_buffer *qbo, *next;
2387ec681f3Smrg   LIST_FOR_EACH_ENTRY_SAFE(qbo, next, &query->buffers, list) {
2397ec681f3Smrg      pipe_resource_reference(&qbo->buffer, NULL);
2407ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++)
2417ec681f3Smrg         pipe_resource_reference(&qbo->xfb_buffers[i], NULL);
2427ec681f3Smrg      FREE(qbo);
2437ec681f3Smrg   }
2447ec681f3Smrg   for (unsigned i = 0; i < ARRAY_SIZE(query->xfb_query_pool); i++) {
2457ec681f3Smrg      if (query->xfb_query_pool[i])
2467ec681f3Smrg         VKSCR(DestroyQueryPool)(screen->dev, query->xfb_query_pool[i], NULL);
2477ec681f3Smrg   }
2487ec681f3Smrg   pipe_resource_reference((struct pipe_resource**)&query->predicate, NULL);
2497ec681f3Smrg   FREE(query);
2507ec681f3Smrg}
2517ec681f3Smrg
2527ec681f3Smrgstatic void
2537ec681f3Smrgreset_qbo(struct zink_query *q)
2547ec681f3Smrg{
2557ec681f3Smrg   q->curr_qbo = list_first_entry(&q->buffers, struct zink_query_buffer, list);
2567ec681f3Smrg   q->curr_qbo->num_results = 0;
2577ec681f3Smrg}
2587ec681f3Smrg
2597ec681f3Smrgstatic struct pipe_query *
2607ec681f3Smrgzink_create_query(struct pipe_context *pctx,
2617ec681f3Smrg                  unsigned query_type, unsigned index)
2627ec681f3Smrg{
2637ec681f3Smrg   struct zink_screen *screen = zink_screen(pctx->screen);
2647ec681f3Smrg   struct zink_query *query = CALLOC_STRUCT(zink_query);
2657ec681f3Smrg   VkQueryPoolCreateInfo pool_create = {0};
2667ec681f3Smrg
2677ec681f3Smrg   if (!query)
2687ec681f3Smrg      return NULL;
2697ec681f3Smrg   list_inithead(&query->buffers);
2707ec681f3Smrg
2717ec681f3Smrg   query->index = index;
2727ec681f3Smrg   query->type = query_type;
2737ec681f3Smrg   if (query->type == PIPE_QUERY_GPU_FINISHED)
2747ec681f3Smrg      return (struct pipe_query *)query;
2757ec681f3Smrg   query->vkqtype = convert_query_type(query_type, &query->precise);
2767ec681f3Smrg   if (query->vkqtype == -1)
2777ec681f3Smrg      return NULL;
2787ec681f3Smrg
2797ec681f3Smrg   assert(!query->precise || query->vkqtype == VK_QUERY_TYPE_OCCLUSION);
2807ec681f3Smrg
2817ec681f3Smrg   query->curr_query = 0;
2827ec681f3Smrg
2837ec681f3Smrg   pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
2847ec681f3Smrg   pool_create.queryType = query->vkqtype;
2857ec681f3Smrg   pool_create.queryCount = NUM_QUERIES;
2867ec681f3Smrg   if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED)
2877ec681f3Smrg     pool_create.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT |
2887ec681f3Smrg                                      VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT;
2897ec681f3Smrg   else if (query_type == PIPE_QUERY_PIPELINE_STATISTICS_SINGLE)
2907ec681f3Smrg      pool_create.pipelineStatistics = pipeline_statistic_convert(index);
2917ec681f3Smrg
2927ec681f3Smrg   VkResult status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &query->query_pool);
2937ec681f3Smrg   if (status != VK_SUCCESS)
2947ec681f3Smrg      goto fail;
2957ec681f3Smrg   if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED) {
2967ec681f3Smrg      /* if xfb is active, we need to use an xfb query, otherwise we need pipeline statistics */
2977ec681f3Smrg      pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
2987ec681f3Smrg      pool_create.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
2997ec681f3Smrg      pool_create.queryCount = NUM_QUERIES;
3007ec681f3Smrg
3017ec681f3Smrg      status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &query->xfb_query_pool[0]);
3027ec681f3Smrg      if (status != VK_SUCCESS)
3037ec681f3Smrg         goto fail;
3047ec681f3Smrg   } else if (query_type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
3057ec681f3Smrg      /* need to monitor all xfb streams */
3067ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(query->xfb_query_pool); i++) {
3077ec681f3Smrg         status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &query->xfb_query_pool[i]);
3087ec681f3Smrg         if (status != VK_SUCCESS)
3097ec681f3Smrg            goto fail;
3107ec681f3Smrg      }
3117ec681f3Smrg   }
3127ec681f3Smrg   if (!qbo_append(pctx->screen, query))
3137ec681f3Smrg      goto fail;
3147ec681f3Smrg   struct zink_batch *batch = &zink_context(pctx)->batch;
3157ec681f3Smrg   batch->has_work = true;
3167ec681f3Smrg   query->needs_reset = true;
3177ec681f3Smrg   if (query->type == PIPE_QUERY_TIMESTAMP) {
3187ec681f3Smrg      query->active = true;
3197ec681f3Smrg      /* defer pool reset until end_query since we're guaranteed to be threadsafe then */
3207ec681f3Smrg      reset_qbo(query);
3217ec681f3Smrg   }
3227ec681f3Smrg   return (struct pipe_query *)query;
3237ec681f3Smrgfail:
3247ec681f3Smrg   destroy_query(screen, query);
3257ec681f3Smrg   return NULL;
3267ec681f3Smrg}
3277ec681f3Smrg
3287ec681f3Smrgstatic void
3297ec681f3Smrgzink_destroy_query(struct pipe_context *pctx,
3307ec681f3Smrg                   struct pipe_query *q)
3317ec681f3Smrg{
3327ec681f3Smrg   struct zink_screen *screen = zink_screen(pctx->screen);
3337ec681f3Smrg   struct zink_query *query = (struct zink_query *)q;
3347ec681f3Smrg
3357ec681f3Smrg   /* only destroy if this query isn't active on any batches,
3367ec681f3Smrg    * otherwise just mark dead and wait
3377ec681f3Smrg    */
3387ec681f3Smrg   if (query->batch_id) {
3397ec681f3Smrg      p_atomic_set(&query->dead, true);
3407ec681f3Smrg      return;
3417ec681f3Smrg   }
3427ec681f3Smrg
3437ec681f3Smrg   destroy_query(screen, query);
3447ec681f3Smrg}
3457ec681f3Smrg
3467ec681f3Smrgvoid
3477ec681f3Smrgzink_prune_query(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_query *query)
3487ec681f3Smrg{
3497ec681f3Smrg   if (!zink_batch_usage_matches(query->batch_id, bs))
3507ec681f3Smrg      return;
3517ec681f3Smrg   query->batch_id = NULL;
3527ec681f3Smrg   if (p_atomic_read(&query->dead))
3537ec681f3Smrg      destroy_query(screen, query);
3547ec681f3Smrg}
3557ec681f3Smrg
3567ec681f3Smrgstatic void
3577ec681f3Smrgcheck_query_results(struct zink_query *query, union pipe_query_result *result,
3587ec681f3Smrg                    int num_results, uint64_t *results, uint64_t *xfb_results)
3597ec681f3Smrg{
3607ec681f3Smrg   uint64_t last_val = 0;
3617ec681f3Smrg   int result_size = get_num_results(query->type);
3627ec681f3Smrg   for (int i = 0; i < num_results * result_size; i += result_size) {
3637ec681f3Smrg      switch (query->type) {
3647ec681f3Smrg      case PIPE_QUERY_OCCLUSION_PREDICATE:
3657ec681f3Smrg      case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
3667ec681f3Smrg      case PIPE_QUERY_GPU_FINISHED:
3677ec681f3Smrg         result->b |= results[i] != 0;
3687ec681f3Smrg         break;
3697ec681f3Smrg
3707ec681f3Smrg      case PIPE_QUERY_TIME_ELAPSED:
3717ec681f3Smrg      case PIPE_QUERY_TIMESTAMP:
3727ec681f3Smrg         /* the application can sum the differences between all N queries to determine the total execution time.
3737ec681f3Smrg          * - 17.5. Timestamp Queries
3747ec681f3Smrg          */
3757ec681f3Smrg         if (query->type != PIPE_QUERY_TIME_ELAPSED || i)
3767ec681f3Smrg            result->u64 += results[i] - last_val;
3777ec681f3Smrg         last_val = results[i];
3787ec681f3Smrg         break;
3797ec681f3Smrg      case PIPE_QUERY_OCCLUSION_COUNTER:
3807ec681f3Smrg         result->u64 += results[i];
3817ec681f3Smrg         break;
3827ec681f3Smrg      case PIPE_QUERY_PRIMITIVES_GENERATED:
3837ec681f3Smrg         if (query->have_xfb[query->last_start + i / 2] || query->index)
3847ec681f3Smrg            result->u64 += xfb_results[i + 1];
3857ec681f3Smrg         else
3867ec681f3Smrg            /* if a given draw had a geometry shader, we need to use the second result */
3877ec681f3Smrg            result->u64 += results[i + query->have_gs[query->last_start + i / 2]];
3887ec681f3Smrg         break;
3897ec681f3Smrg      case PIPE_QUERY_PRIMITIVES_EMITTED:
3907ec681f3Smrg         /* A query pool created with this type will capture 2 integers -
3917ec681f3Smrg          * numPrimitivesWritten and numPrimitivesNeeded -
3927ec681f3Smrg          * for the specified vertex stream output from the last vertex processing stage.
3937ec681f3Smrg          * - from VK_EXT_transform_feedback spec
3947ec681f3Smrg          */
3957ec681f3Smrg         result->u64 += results[i];
3967ec681f3Smrg         break;
3977ec681f3Smrg      case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
3987ec681f3Smrg      case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
3997ec681f3Smrg         /* A query pool created with this type will capture 2 integers -
4007ec681f3Smrg          * numPrimitivesWritten and numPrimitivesNeeded -
4017ec681f3Smrg          * for the specified vertex stream output from the last vertex processing stage.
4027ec681f3Smrg          * - from VK_EXT_transform_feedback spec
4037ec681f3Smrg          */
4047ec681f3Smrg         if (query->have_xfb[query->last_start + i / 2])
4057ec681f3Smrg            result->b |= results[i] != results[i + 1];
4067ec681f3Smrg         break;
4077ec681f3Smrg      case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
4087ec681f3Smrg         result->u64 += results[i];
4097ec681f3Smrg         break;
4107ec681f3Smrg
4117ec681f3Smrg      default:
4127ec681f3Smrg         debug_printf("unhandled query type: %s\n",
4137ec681f3Smrg                      util_str_query_type(query->type, true));
4147ec681f3Smrg         unreachable("unexpected query type");
4157ec681f3Smrg      }
4167ec681f3Smrg   }
4177ec681f3Smrg}
4187ec681f3Smrg
4197ec681f3Smrgstatic bool
4207ec681f3Smrgget_query_result(struct pipe_context *pctx,
4217ec681f3Smrg                      struct pipe_query *q,
4227ec681f3Smrg                      bool wait,
4237ec681f3Smrg                      union pipe_query_result *result)
4247ec681f3Smrg{
4257ec681f3Smrg   struct zink_screen *screen = zink_screen(pctx->screen);
4267ec681f3Smrg   struct zink_query *query = (struct zink_query *)q;
4277ec681f3Smrg   unsigned flags = PIPE_MAP_READ;
4287ec681f3Smrg
4297ec681f3Smrg   if (!wait)
4307ec681f3Smrg      flags |= PIPE_MAP_DONTBLOCK;
4317ec681f3Smrg   if (query->base.flushed)
4327ec681f3Smrg      /* this is not a context-safe operation; ensure map doesn't use slab alloc */
4337ec681f3Smrg      flags |= PIPE_MAP_THREAD_SAFE;
4347ec681f3Smrg
4357ec681f3Smrg   util_query_clear_result(result, query->type);
4367ec681f3Smrg
4377ec681f3Smrg   int num_results = query->curr_query - query->last_start;
4387ec681f3Smrg   int result_size = get_num_results(query->type) * sizeof(uint64_t);
4397ec681f3Smrg
4407ec681f3Smrg   struct zink_query_buffer *qbo;
4417ec681f3Smrg   struct pipe_transfer *xfer;
4427ec681f3Smrg   LIST_FOR_EACH_ENTRY(qbo, &query->buffers, list) {
4437ec681f3Smrg      uint64_t *xfb_results = NULL;
4447ec681f3Smrg      uint64_t *results;
4457ec681f3Smrg      bool is_timestamp = query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
4467ec681f3Smrg      if (!qbo->num_results)
4477ec681f3Smrg         continue;
4487ec681f3Smrg      results = pipe_buffer_map_range(pctx, qbo->buffer, 0,
4497ec681f3Smrg                                      (is_timestamp ? 1 : qbo->num_results) * result_size, flags, &xfer);
4507ec681f3Smrg      if (!results) {
4517ec681f3Smrg         if (wait)
4527ec681f3Smrg            debug_printf("zink: qbo read failed!");
4537ec681f3Smrg         return false;
4547ec681f3Smrg      }
4557ec681f3Smrg      struct pipe_transfer *xfb_xfer = NULL;
4567ec681f3Smrg      if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) {
4577ec681f3Smrg         xfb_results = pipe_buffer_map_range(pctx, qbo->xfb_buffers[0], 0,
4587ec681f3Smrg                                         qbo->num_results * result_size, flags, &xfb_xfer);
4597ec681f3Smrg         if (!xfb_results) {
4607ec681f3Smrg            if (wait)
4617ec681f3Smrg               debug_printf("zink: xfb qbo read failed!");
4627ec681f3Smrg            pipe_buffer_unmap(pctx, xfer);
4637ec681f3Smrg            return false;
4647ec681f3Smrg         }
4657ec681f3Smrg      }
4667ec681f3Smrg      check_query_results(query, result, is_timestamp ? 1 : qbo->num_results, results, xfb_results);
4677ec681f3Smrg      pipe_buffer_unmap(pctx, xfer);
4687ec681f3Smrg      if (xfb_xfer)
4697ec681f3Smrg         pipe_buffer_unmap(pctx, xfb_xfer);
4707ec681f3Smrg      if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
4717ec681f3Smrg         for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers) && !result->b; i++) {
4727ec681f3Smrg            uint64_t *results = pipe_buffer_map_range(pctx, qbo->xfb_buffers[i],
4737ec681f3Smrg                                              0,
4747ec681f3Smrg                                              qbo->num_results * result_size, flags, &xfer);
4757ec681f3Smrg            if (!results) {
4767ec681f3Smrg               if (wait)
4777ec681f3Smrg                  debug_printf("zink: qbo read failed!");
4787ec681f3Smrg               return false;
4797ec681f3Smrg            }
4807ec681f3Smrg            check_query_results(query, result, num_results, results, xfb_results);
4817ec681f3Smrg            pipe_buffer_unmap(pctx, xfer);
4827ec681f3Smrg         }
4837ec681f3Smrg         /* if overflow is detected we can stop */
4847ec681f3Smrg         if (result->b)
4857ec681f3Smrg            break;
4867ec681f3Smrg      }
4877ec681f3Smrg   }
4887ec681f3Smrg
4897ec681f3Smrg   if (is_time_query(query))
4907ec681f3Smrg      timestamp_to_nanoseconds(screen, &result->u64);
4917ec681f3Smrg
4927ec681f3Smrg   return true;
4937ec681f3Smrg}
4947ec681f3Smrg
4957ec681f3Smrgstatic void
4967ec681f3Smrgforce_cpu_read(struct zink_context *ctx, struct pipe_query *pquery, enum pipe_query_value_type result_type, struct pipe_resource *pres, unsigned offset)
4977ec681f3Smrg{
4987ec681f3Smrg   struct pipe_context *pctx = &ctx->base;
4997ec681f3Smrg   unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t);
5007ec681f3Smrg   struct zink_query *query = (struct zink_query*)pquery;
5017ec681f3Smrg   union pipe_query_result result;
5027ec681f3Smrg
5037ec681f3Smrg   if (query->needs_update)
5047ec681f3Smrg      update_qbo(ctx, query);
5057ec681f3Smrg
5067ec681f3Smrg   bool success = get_query_result(pctx, pquery, true, &result);
5077ec681f3Smrg   if (!success) {
5087ec681f3Smrg      debug_printf("zink: getting query result failed\n");
5097ec681f3Smrg      return;
5107ec681f3Smrg   }
5117ec681f3Smrg
5127ec681f3Smrg   if (result_type <= PIPE_QUERY_TYPE_U32) {
5137ec681f3Smrg      uint32_t u32;
5147ec681f3Smrg      uint32_t limit;
5157ec681f3Smrg      if (result_type == PIPE_QUERY_TYPE_I32)
5167ec681f3Smrg         limit = INT_MAX;
5177ec681f3Smrg      else
5187ec681f3Smrg         limit = UINT_MAX;
5197ec681f3Smrg      if (is_bool_query(query))
5207ec681f3Smrg         u32 = result.b;
5217ec681f3Smrg      else
5227ec681f3Smrg         u32 = MIN2(limit, result.u64);
5237ec681f3Smrg      pipe_buffer_write(pctx, pres, offset, result_size, &u32);
5247ec681f3Smrg   } else {
5257ec681f3Smrg      uint64_t u64;
5267ec681f3Smrg      if (is_bool_query(query))
5277ec681f3Smrg         u64 = result.b;
5287ec681f3Smrg      else
5297ec681f3Smrg         u64 = result.u64;
5307ec681f3Smrg      pipe_buffer_write(pctx, pres, offset, result_size, &u64);
5317ec681f3Smrg   }
5327ec681f3Smrg}
5337ec681f3Smrg
5347ec681f3Smrgstatic void
5357ec681f3Smrgcopy_pool_results_to_buffer(struct zink_context *ctx, struct zink_query *query, VkQueryPool pool,
5367ec681f3Smrg                            unsigned query_id, struct zink_resource *res, unsigned offset,
5377ec681f3Smrg                            int num_results, VkQueryResultFlags flags)
5387ec681f3Smrg{
5397ec681f3Smrg   struct zink_batch *batch = &ctx->batch;
5407ec681f3Smrg   unsigned type_size = (flags & VK_QUERY_RESULT_64_BIT) ? sizeof(uint64_t) : sizeof(uint32_t);
5417ec681f3Smrg   unsigned base_result_size = get_num_results(query->type) * type_size;
5427ec681f3Smrg   unsigned result_size = base_result_size * num_results;
5437ec681f3Smrg   if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)
5447ec681f3Smrg      result_size += type_size;
5457ec681f3Smrg   zink_batch_no_rp(ctx);
5467ec681f3Smrg   /* if it's a single query that doesn't need special handling, we can copy it and be done */
5477ec681f3Smrg   zink_batch_reference_resource_rw(batch, res, true);
5487ec681f3Smrg   zink_resource_buffer_barrier(ctx, res, VK_ACCESS_TRANSFER_WRITE_BIT, 0);
5497ec681f3Smrg   util_range_add(&res->base.b, &res->valid_buffer_range, offset, offset + result_size);
5507ec681f3Smrg   assert(query_id < NUM_QUERIES);
5517ec681f3Smrg   VKCTX(CmdCopyQueryPoolResults)(batch->state->cmdbuf, pool, query_id, num_results, res->obj->buffer,
5527ec681f3Smrg                             offset, base_result_size, flags);
5537ec681f3Smrg}
5547ec681f3Smrg
5557ec681f3Smrgstatic void
5567ec681f3Smrgcopy_results_to_buffer(struct zink_context *ctx, struct zink_query *query, struct zink_resource *res, unsigned offset, int num_results, VkQueryResultFlags flags)
5577ec681f3Smrg{
5587ec681f3Smrg   copy_pool_results_to_buffer(ctx, query, query->query_pool, query->last_start, res, offset, num_results, flags);
5597ec681f3Smrg}
5607ec681f3Smrg
5617ec681f3Smrgstatic void
5627ec681f3Smrgreset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
5637ec681f3Smrg{
5647ec681f3Smrg   /* This command must only be called outside of a render pass instance
5657ec681f3Smrg    *
5667ec681f3Smrg    * - vkCmdResetQueryPool spec
5677ec681f3Smrg    */
5687ec681f3Smrg   zink_batch_no_rp(ctx);
5697ec681f3Smrg   if (q->needs_update)
5707ec681f3Smrg      update_qbo(ctx, q);
5717ec681f3Smrg
5727ec681f3Smrg   VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, q->query_pool, 0, NUM_QUERIES);
5737ec681f3Smrg   if (q->type == PIPE_QUERY_PRIMITIVES_GENERATED)
5747ec681f3Smrg      VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, q->xfb_query_pool[0], 0, NUM_QUERIES);
5757ec681f3Smrg   else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
5767ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++)
5777ec681f3Smrg         VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, q->xfb_query_pool[i], 0, NUM_QUERIES);
5787ec681f3Smrg   }
5797ec681f3Smrg   memset(q->have_gs, 0, sizeof(q->have_gs));
5807ec681f3Smrg   memset(q->have_xfb, 0, sizeof(q->have_xfb));
5817ec681f3Smrg   q->last_start = q->curr_query = 0;
5827ec681f3Smrg   q->needs_reset = false;
5837ec681f3Smrg   /* create new qbo for non-timestamp queries:
5847ec681f3Smrg    * timestamp queries should never need more than 2 entries in the qbo
5857ec681f3Smrg    */
5867ec681f3Smrg   if (q->type == PIPE_QUERY_TIMESTAMP)
5877ec681f3Smrg      return;
5887ec681f3Smrg   if (qbo_append(ctx->base.screen, q))
5897ec681f3Smrg      reset_qbo(q);
5907ec681f3Smrg   else
5917ec681f3Smrg      debug_printf("zink: qbo alloc failed on reset!");
5927ec681f3Smrg}
5937ec681f3Smrg
5947ec681f3Smrgstatic inline unsigned
5957ec681f3Smrgget_buffer_offset(struct zink_query *q, struct pipe_resource *pres, unsigned query_id)
5967ec681f3Smrg{
5977ec681f3Smrg   return (query_id - q->last_start) * get_num_results(q->type) * sizeof(uint64_t);
5987ec681f3Smrg}
5997ec681f3Smrg
6007ec681f3Smrgstatic void
6017ec681f3Smrgupdate_qbo(struct zink_context *ctx, struct zink_query *q)
6027ec681f3Smrg{
6037ec681f3Smrg   struct zink_query_buffer *qbo = q->curr_qbo;
6047ec681f3Smrg   unsigned offset = 0;
6057ec681f3Smrg   uint32_t query_id = q->curr_query - 1;
6067ec681f3Smrg   bool is_timestamp = q->type == PIPE_QUERY_TIMESTAMP || q->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
6077ec681f3Smrg   /* timestamp queries just write to offset 0 always */
6087ec681f3Smrg   if (!is_timestamp)
6097ec681f3Smrg      offset = get_buffer_offset(q, qbo->buffer, query_id);
6107ec681f3Smrg   copy_pool_results_to_buffer(ctx, q, q->query_pool, query_id, zink_resource(qbo->buffer),
6117ec681f3Smrg                          offset,
6127ec681f3Smrg                          1, VK_QUERY_RESULT_64_BIT);
6137ec681f3Smrg
6147ec681f3Smrg   if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
6157ec681f3Smrg       q->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
6167ec681f3Smrg       q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
6177ec681f3Smrg      copy_pool_results_to_buffer(ctx, q,
6187ec681f3Smrg                                  q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool,
6197ec681f3Smrg                                  query_id,
6207ec681f3Smrg                                  zink_resource(qbo->xfb_buffers[0] ? qbo->xfb_buffers[0] : qbo->buffer),
6217ec681f3Smrg                             get_buffer_offset(q, qbo->xfb_buffers[0] ? qbo->xfb_buffers[0] : qbo->buffer, query_id),
6227ec681f3Smrg                             1, VK_QUERY_RESULT_64_BIT);
6237ec681f3Smrg   }
6247ec681f3Smrg
6257ec681f3Smrg   else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
6267ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) {
6277ec681f3Smrg         copy_pool_results_to_buffer(ctx, q, q->xfb_query_pool[i], query_id, zink_resource(qbo->xfb_buffers[i]),
6287ec681f3Smrg                                get_buffer_offset(q, qbo->xfb_buffers[i], query_id),
6297ec681f3Smrg                                1, VK_QUERY_RESULT_64_BIT);
6307ec681f3Smrg      }
6317ec681f3Smrg   }
6327ec681f3Smrg
6337ec681f3Smrg   if (!is_timestamp)
6347ec681f3Smrg      q->curr_qbo->num_results++;
6357ec681f3Smrg   else
6367ec681f3Smrg      q->curr_qbo->num_results = 1;
6377ec681f3Smrg   q->needs_update = false;
6387ec681f3Smrg}
6397ec681f3Smrg
6407ec681f3Smrgstatic void
6417ec681f3Smrgbegin_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
6427ec681f3Smrg{
6437ec681f3Smrg   VkQueryControlFlags flags = 0;
6447ec681f3Smrg
6457ec681f3Smrg   q->predicate_dirty = true;
6467ec681f3Smrg   if (q->needs_reset)
6477ec681f3Smrg      reset_pool(ctx, batch, q);
6487ec681f3Smrg   assert(q->curr_query < NUM_QUERIES);
6497ec681f3Smrg   q->active = true;
6507ec681f3Smrg   batch->has_work = true;
6517ec681f3Smrg   if (q->type == PIPE_QUERY_TIME_ELAPSED) {
6527ec681f3Smrg      VKCTX(CmdWriteTimestamp)(batch->state->cmdbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, q->query_pool, q->curr_query);
6537ec681f3Smrg      q->curr_query++;
6547ec681f3Smrg      update_qbo(ctx, q);
6557ec681f3Smrg      zink_batch_usage_set(&q->batch_id, batch->state);
6567ec681f3Smrg      _mesa_set_add(batch->state->active_queries, q);
6577ec681f3Smrg   }
6587ec681f3Smrg   /* ignore the rest of begin_query for timestamps */
6597ec681f3Smrg   if (is_time_query(q))
6607ec681f3Smrg      return;
6617ec681f3Smrg   if (q->precise)
6627ec681f3Smrg      flags |= VK_QUERY_CONTROL_PRECISE_BIT;
6637ec681f3Smrg   if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
6647ec681f3Smrg       q->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
6657ec681f3Smrg       q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
6667ec681f3Smrg      VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf,
6677ec681f3Smrg                                     q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool,
6687ec681f3Smrg                                     q->curr_query,
6697ec681f3Smrg                                     flags,
6707ec681f3Smrg                                     q->index);
6717ec681f3Smrg      q->xfb_running = true;
6727ec681f3Smrg   } else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
6737ec681f3Smrg      VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf,
6747ec681f3Smrg                                     q->query_pool,
6757ec681f3Smrg                                     q->curr_query,
6767ec681f3Smrg                                     flags,
6777ec681f3Smrg                                     0);
6787ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++)
6797ec681f3Smrg         VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf,
6807ec681f3Smrg                                        q->xfb_query_pool[i],
6817ec681f3Smrg                                        q->curr_query,
6827ec681f3Smrg                                        flags,
6837ec681f3Smrg                                        i + 1);
6847ec681f3Smrg      q->xfb_running = true;
6857ec681f3Smrg   }
6867ec681f3Smrg   if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
6877ec681f3Smrg      VKCTX(CmdBeginQuery)(batch->state->cmdbuf, q->query_pool, q->curr_query, flags);
6887ec681f3Smrg   if (needs_stats_list(q))
6897ec681f3Smrg      list_addtail(&q->stats_list, &ctx->primitives_generated_queries);
6907ec681f3Smrg   zink_batch_usage_set(&q->batch_id, batch->state);
6917ec681f3Smrg   _mesa_set_add(batch->state->active_queries, q);
6927ec681f3Smrg}
6937ec681f3Smrg
6947ec681f3Smrgstatic bool
6957ec681f3Smrgzink_begin_query(struct pipe_context *pctx,
6967ec681f3Smrg                 struct pipe_query *q)
6977ec681f3Smrg{
6987ec681f3Smrg   struct zink_query *query = (struct zink_query *)q;
6997ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
7007ec681f3Smrg   struct zink_batch *batch = &ctx->batch;
7017ec681f3Smrg
7027ec681f3Smrg   query->last_start = query->curr_query;
7037ec681f3Smrg   /* drop all past results */
7047ec681f3Smrg   reset_qbo(query);
7057ec681f3Smrg
7067ec681f3Smrg   begin_query(ctx, batch, query);
7077ec681f3Smrg
7087ec681f3Smrg   return true;
7097ec681f3Smrg}
7107ec681f3Smrg
7117ec681f3Smrgstatic void
7127ec681f3Smrgupdate_query_id(struct zink_context *ctx, struct zink_query *q)
7137ec681f3Smrg{
7147ec681f3Smrg   if (++q->curr_query == NUM_QUERIES) {
7157ec681f3Smrg      /* always reset on start; this ensures we can actually submit the batch that the current query is on */
7167ec681f3Smrg      q->needs_reset = true;
7177ec681f3Smrg   }
7187ec681f3Smrg   ctx->batch.has_work = true;
7197ec681f3Smrg
7207ec681f3Smrg   if (ctx->batch.in_rp)
7217ec681f3Smrg      q->needs_update = true;
7227ec681f3Smrg   else
7237ec681f3Smrg      update_qbo(ctx, q);
7247ec681f3Smrg}
7257ec681f3Smrg
7267ec681f3Smrgstatic void
7277ec681f3Smrgend_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
7287ec681f3Smrg{
7297ec681f3Smrg   ASSERTED struct zink_query_buffer *qbo = q->curr_qbo;
7307ec681f3Smrg   assert(qbo);
7317ec681f3Smrg   assert(!is_time_query(q));
7327ec681f3Smrg   q->active = false;
7337ec681f3Smrg   if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
7347ec681f3Smrg            q->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
7357ec681f3Smrg            q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
7367ec681f3Smrg      VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf,
7377ec681f3Smrg                                   q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool,
7387ec681f3Smrg                                   q->curr_query, q->index);
7397ec681f3Smrg   }
7407ec681f3Smrg
7417ec681f3Smrg   else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
7427ec681f3Smrg      VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf, q->query_pool, q->curr_query, 0);
7437ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) {
7447ec681f3Smrg         VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf, q->xfb_query_pool[i], q->curr_query, i + 1);
7457ec681f3Smrg      }
7467ec681f3Smrg   }
7477ec681f3Smrg   if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT && !is_time_query(q))
7487ec681f3Smrg      VKCTX(CmdEndQuery)(batch->state->cmdbuf, q->query_pool, q->curr_query);
7497ec681f3Smrg
7507ec681f3Smrg   if (needs_stats_list(q))
7517ec681f3Smrg      list_delinit(&q->stats_list);
7527ec681f3Smrg
7537ec681f3Smrg   update_query_id(ctx, q);
7547ec681f3Smrg}
7557ec681f3Smrg
7567ec681f3Smrgstatic bool
7577ec681f3Smrgzink_end_query(struct pipe_context *pctx,
7587ec681f3Smrg               struct pipe_query *q)
7597ec681f3Smrg{
7607ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
7617ec681f3Smrg   struct zink_query *query = (struct zink_query *)q;
7627ec681f3Smrg   struct zink_batch *batch = &ctx->batch;
7637ec681f3Smrg
7647ec681f3Smrg   if (query->type == PIPE_QUERY_GPU_FINISHED) {
7657ec681f3Smrg      pctx->flush(pctx, &query->fence, PIPE_FLUSH_DEFERRED);
7667ec681f3Smrg      return true;
7677ec681f3Smrg   }
7687ec681f3Smrg
7697ec681f3Smrg   /* FIXME: this can be called from a thread, but it needs to write to the cmdbuf */
7707ec681f3Smrg   threaded_context_unwrap_sync(pctx);
7717ec681f3Smrg
7727ec681f3Smrg   if (needs_stats_list(query))
7737ec681f3Smrg      list_delinit(&query->stats_list);
7747ec681f3Smrg   if (is_time_query(query)) {
7757ec681f3Smrg      if (query->needs_reset)
7767ec681f3Smrg         reset_pool(ctx, batch, query);
7777ec681f3Smrg      VKCTX(CmdWriteTimestamp)(batch->state->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
7787ec681f3Smrg                          query->query_pool, query->curr_query);
7797ec681f3Smrg      zink_batch_usage_set(&query->batch_id, batch->state);
7807ec681f3Smrg      _mesa_set_add(batch->state->active_queries, query);
7817ec681f3Smrg      update_query_id(ctx, query);
7827ec681f3Smrg   } else if (query->active)
7837ec681f3Smrg      end_query(ctx, batch, query);
7847ec681f3Smrg
7857ec681f3Smrg   return true;
7867ec681f3Smrg}
7877ec681f3Smrg
7887ec681f3Smrgstatic bool
7897ec681f3Smrgzink_get_query_result(struct pipe_context *pctx,
7907ec681f3Smrg                      struct pipe_query *q,
7917ec681f3Smrg                      bool wait,
7927ec681f3Smrg                      union pipe_query_result *result)
7937ec681f3Smrg{
7947ec681f3Smrg   struct zink_query *query = (void*)q;
7957ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
7967ec681f3Smrg
7977ec681f3Smrg   if (query->type == PIPE_QUERY_GPU_FINISHED) {
7987ec681f3Smrg      struct pipe_screen *screen = pctx->screen;
7997ec681f3Smrg
8007ec681f3Smrg      result->b = screen->fence_finish(screen, query->base.flushed ? NULL : pctx,
8017ec681f3Smrg                                        query->fence, wait ? PIPE_TIMEOUT_INFINITE : 0);
8027ec681f3Smrg      return result->b;
8037ec681f3Smrg   }
8047ec681f3Smrg
8057ec681f3Smrg   if (query->needs_update)
8067ec681f3Smrg      update_qbo(ctx, query);
8077ec681f3Smrg
8087ec681f3Smrg   if (zink_batch_usage_is_unflushed(query->batch_id)) {
8097ec681f3Smrg      if (!threaded_query(q)->flushed)
8107ec681f3Smrg         pctx->flush(pctx, NULL, 0);
8117ec681f3Smrg      if (!wait)
8127ec681f3Smrg         return false;
8137ec681f3Smrg   } else if (!threaded_query(q)->flushed &&
8147ec681f3Smrg              /* timeline drivers can wait during buffer map */
8157ec681f3Smrg              !zink_screen(pctx->screen)->info.have_KHR_timeline_semaphore)
8167ec681f3Smrg      zink_batch_usage_check_completion(ctx, query->batch_id);
8177ec681f3Smrg
8187ec681f3Smrg   return get_query_result(pctx, q, wait, result);
8197ec681f3Smrg}
8207ec681f3Smrg
8217ec681f3Smrgvoid
8227ec681f3Smrgzink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch)
8237ec681f3Smrg{
8247ec681f3Smrg   set_foreach(batch->state->active_queries, entry) {
8257ec681f3Smrg      struct zink_query *query = (void*)entry->key;
8267ec681f3Smrg      /* if a query isn't active here then we don't need to reactivate it on the next batch */
8277ec681f3Smrg      if (query->active && !is_time_query(query)) {
8287ec681f3Smrg         end_query(ctx, batch, query);
8297ec681f3Smrg         /* the fence is going to steal the set off the batch, so we have to copy
8307ec681f3Smrg          * the active queries onto a list
8317ec681f3Smrg          */
8327ec681f3Smrg         list_addtail(&query->active_list, &ctx->suspended_queries);
8337ec681f3Smrg      }
8347ec681f3Smrg      if (query->needs_update)
8357ec681f3Smrg         update_qbo(ctx, query);
8367ec681f3Smrg      if (query->last_start && query->curr_query > NUM_QUERIES / 2)
8377ec681f3Smrg         reset_pool(ctx, batch, query);
8387ec681f3Smrg   }
8397ec681f3Smrg}
8407ec681f3Smrg
8417ec681f3Smrgvoid
8427ec681f3Smrgzink_resume_queries(struct zink_context *ctx, struct zink_batch *batch)
8437ec681f3Smrg{
8447ec681f3Smrg   struct zink_query *query, *next;
8457ec681f3Smrg   LIST_FOR_EACH_ENTRY_SAFE(query, next, &ctx->suspended_queries, active_list) {
8467ec681f3Smrg      begin_query(ctx, batch, query);
8477ec681f3Smrg      list_delinit(&query->active_list);
8487ec681f3Smrg   }
8497ec681f3Smrg}
8507ec681f3Smrg
8517ec681f3Smrgvoid
8527ec681f3Smrgzink_query_update_gs_states(struct zink_context *ctx)
8537ec681f3Smrg{
8547ec681f3Smrg   struct zink_query *query;
8557ec681f3Smrg   LIST_FOR_EACH_ENTRY(query, &ctx->primitives_generated_queries, stats_list) {
8567ec681f3Smrg      assert(query->curr_query < ARRAY_SIZE(query->have_gs));
8577ec681f3Smrg      assert(query->active);
8587ec681f3Smrg      query->have_gs[query->curr_query] = !!ctx->gfx_stages[PIPE_SHADER_GEOMETRY];
8597ec681f3Smrg      query->have_xfb[query->curr_query] = !!ctx->num_so_targets;
8607ec681f3Smrg   }
8617ec681f3Smrg}
8627ec681f3Smrg
8637ec681f3Smrgstatic void
8647ec681f3Smrgzink_set_active_query_state(struct pipe_context *pctx, bool enable)
8657ec681f3Smrg{
8667ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
8677ec681f3Smrg   ctx->queries_disabled = !enable;
8687ec681f3Smrg
8697ec681f3Smrg   struct zink_batch *batch = &ctx->batch;
8707ec681f3Smrg   if (ctx->queries_disabled)
8717ec681f3Smrg      zink_suspend_queries(ctx, batch);
8727ec681f3Smrg   else
8737ec681f3Smrg      zink_resume_queries(ctx, batch);
8747ec681f3Smrg}
8757ec681f3Smrg
8767ec681f3Smrgvoid
8777ec681f3Smrgzink_start_conditional_render(struct zink_context *ctx)
8787ec681f3Smrg{
8797ec681f3Smrg   if (unlikely(!zink_screen(ctx->base.screen)->info.have_EXT_conditional_rendering))
8807ec681f3Smrg      return;
8817ec681f3Smrg   struct zink_batch *batch = &ctx->batch;
8827ec681f3Smrg   VkConditionalRenderingFlagsEXT begin_flags = 0;
8837ec681f3Smrg   if (ctx->render_condition.inverted)
8847ec681f3Smrg      begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
8857ec681f3Smrg   VkConditionalRenderingBeginInfoEXT begin_info = {0};
8867ec681f3Smrg   begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
8877ec681f3Smrg   begin_info.buffer = ctx->render_condition.query->predicate->obj->buffer;
8887ec681f3Smrg   begin_info.flags = begin_flags;
8897ec681f3Smrg   VKCTX(CmdBeginConditionalRenderingEXT)(batch->state->cmdbuf, &begin_info);
8907ec681f3Smrg   zink_batch_reference_resource_rw(batch, ctx->render_condition.query->predicate, false);
8917ec681f3Smrg}
8927ec681f3Smrg
8937ec681f3Smrgvoid
8947ec681f3Smrgzink_stop_conditional_render(struct zink_context *ctx)
8957ec681f3Smrg{
8967ec681f3Smrg   struct zink_batch *batch = &ctx->batch;
8977ec681f3Smrg   zink_clear_apply_conditionals(ctx);
8987ec681f3Smrg   if (unlikely(!zink_screen(ctx->base.screen)->info.have_EXT_conditional_rendering))
8997ec681f3Smrg      return;
9007ec681f3Smrg   VKCTX(CmdEndConditionalRenderingEXT)(batch->state->cmdbuf);
9017ec681f3Smrg}
9027ec681f3Smrg
9037ec681f3Smrgbool
9047ec681f3Smrgzink_check_conditional_render(struct zink_context *ctx)
9057ec681f3Smrg{
9067ec681f3Smrg   if (!ctx->render_condition_active)
9077ec681f3Smrg      return true;
9087ec681f3Smrg   assert(ctx->render_condition.query);
9097ec681f3Smrg
9107ec681f3Smrg   union pipe_query_result result;
9117ec681f3Smrg   zink_get_query_result(&ctx->base, (struct pipe_query*)ctx->render_condition.query, true, &result);
9127ec681f3Smrg   return is_bool_query(ctx->render_condition.query) ?
9137ec681f3Smrg          ctx->render_condition.inverted != result.b :
9147ec681f3Smrg          ctx->render_condition.inverted != !!result.u64;
9157ec681f3Smrg}
9167ec681f3Smrg
9177ec681f3Smrgstatic void
9187ec681f3Smrgzink_render_condition(struct pipe_context *pctx,
9197ec681f3Smrg                      struct pipe_query *pquery,
9207ec681f3Smrg                      bool condition,
9217ec681f3Smrg                      enum pipe_render_cond_flag mode)
9227ec681f3Smrg{
9237ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
9247ec681f3Smrg   struct zink_query *query = (struct zink_query *)pquery;
9257ec681f3Smrg   zink_batch_no_rp(ctx);
9267ec681f3Smrg   VkQueryResultFlagBits flags = 0;
9277ec681f3Smrg
9287ec681f3Smrg   if (query == NULL) {
9297ec681f3Smrg      /* force conditional clears if they exist */
9307ec681f3Smrg      if (ctx->clears_enabled && !ctx->batch.in_rp)
9317ec681f3Smrg         zink_batch_rp(ctx);
9327ec681f3Smrg      if (ctx->batch.in_rp)
9337ec681f3Smrg         zink_stop_conditional_render(ctx);
9347ec681f3Smrg      ctx->render_condition_active = false;
9357ec681f3Smrg      ctx->render_condition.query = NULL;
9367ec681f3Smrg      return;
9377ec681f3Smrg   }
9387ec681f3Smrg
9397ec681f3Smrg   if (!query->predicate) {
9407ec681f3Smrg      struct pipe_resource *pres;
9417ec681f3Smrg
9427ec681f3Smrg      /* need to create a vulkan buffer to copy the data into */
9437ec681f3Smrg      pres = pipe_buffer_create(pctx->screen, PIPE_BIND_QUERY_BUFFER, PIPE_USAGE_DEFAULT, sizeof(uint64_t));
9447ec681f3Smrg      if (!pres)
9457ec681f3Smrg         return;
9467ec681f3Smrg
9477ec681f3Smrg      query->predicate = zink_resource(pres);
9487ec681f3Smrg   }
9497ec681f3Smrg   if (query->predicate_dirty) {
9507ec681f3Smrg      struct zink_resource *res = query->predicate;
9517ec681f3Smrg
9527ec681f3Smrg      if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT)
9537ec681f3Smrg         flags |= VK_QUERY_RESULT_WAIT_BIT;
9547ec681f3Smrg
9557ec681f3Smrg      flags |= VK_QUERY_RESULT_64_BIT;
9567ec681f3Smrg      int num_results = query->curr_query - query->last_start;
9577ec681f3Smrg      if (query->type != PIPE_QUERY_PRIMITIVES_GENERATED &&
9587ec681f3Smrg          !is_so_overflow_query(query)) {
9597ec681f3Smrg         copy_results_to_buffer(ctx, query, res, 0, num_results, flags);
9607ec681f3Smrg      } else {
9617ec681f3Smrg         /* these need special handling */
9627ec681f3Smrg         force_cpu_read(ctx, pquery, PIPE_QUERY_TYPE_U32, &res->base.b, 0);
9637ec681f3Smrg      }
9647ec681f3Smrg      query->predicate_dirty = false;
9657ec681f3Smrg   }
9667ec681f3Smrg   ctx->render_condition.inverted = condition;
9677ec681f3Smrg   ctx->render_condition_active = true;
9687ec681f3Smrg   ctx->render_condition.query = query;
9697ec681f3Smrg   if (ctx->batch.in_rp)
9707ec681f3Smrg      zink_start_conditional_render(ctx);
9717ec681f3Smrg}
9727ec681f3Smrg
9737ec681f3Smrgstatic void
9747ec681f3Smrgzink_get_query_result_resource(struct pipe_context *pctx,
9757ec681f3Smrg                               struct pipe_query *pquery,
9767ec681f3Smrg                               bool wait,
9777ec681f3Smrg                               enum pipe_query_value_type result_type,
9787ec681f3Smrg                               int index,
9797ec681f3Smrg                               struct pipe_resource *pres,
9807ec681f3Smrg                               unsigned offset)
9817ec681f3Smrg{
9827ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
9837ec681f3Smrg   struct zink_screen *screen = zink_screen(pctx->screen);
9847ec681f3Smrg   struct zink_query *query = (struct zink_query*)pquery;
9857ec681f3Smrg   struct zink_resource *res = zink_resource(pres);
9867ec681f3Smrg   unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t);
9877ec681f3Smrg   VkQueryResultFlagBits size_flags = result_type <= PIPE_QUERY_TYPE_U32 ? 0 : VK_QUERY_RESULT_64_BIT;
9887ec681f3Smrg   unsigned num_queries = query->curr_query - query->last_start;
9897ec681f3Smrg   unsigned query_id = query->last_start;
9907ec681f3Smrg
9917ec681f3Smrg   if (index == -1) {
9927ec681f3Smrg      /* VK_QUERY_RESULT_WITH_AVAILABILITY_BIT will ALWAYS write some kind of result data
9937ec681f3Smrg       * in addition to the availability result, which is a problem if we're just trying to get availability data
9947ec681f3Smrg       *
9957ec681f3Smrg       * if we know that there's no valid buffer data in the preceding buffer range, then we can just
9967ec681f3Smrg       * stomp on it with a glorious queued buffer copy instead of forcing a stall to manually write to the
9977ec681f3Smrg       * buffer
9987ec681f3Smrg       */
9997ec681f3Smrg
10007ec681f3Smrg      VkQueryResultFlags flag = is_time_query(query) ? 0 : VK_QUERY_RESULT_PARTIAL_BIT;
10017ec681f3Smrg      unsigned src_offset = result_size * get_num_results(query->type);
10027ec681f3Smrg      if (zink_batch_usage_check_completion(ctx, query->batch_id)) {
10037ec681f3Smrg         uint64_t u64[4] = {0};
10047ec681f3Smrg         if (VKCTX(GetQueryPoolResults)(screen->dev, query->query_pool, query_id, 1, sizeof(u64), u64,
10057ec681f3Smrg                                   0, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag) == VK_SUCCESS) {
10067ec681f3Smrg            pipe_buffer_write(pctx, pres, offset, result_size, (unsigned char*)u64 + src_offset);
10077ec681f3Smrg            return;
10087ec681f3Smrg         }
10097ec681f3Smrg      }
10107ec681f3Smrg      struct pipe_resource *staging = pipe_buffer_create(pctx->screen, 0, PIPE_USAGE_STAGING, src_offset + result_size);
10117ec681f3Smrg      copy_results_to_buffer(ctx, query, zink_resource(staging), 0, 1, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag);
10127ec681f3Smrg      zink_copy_buffer(ctx, res, zink_resource(staging), offset, result_size * get_num_results(query->type), result_size);
10137ec681f3Smrg      pipe_resource_reference(&staging, NULL);
10147ec681f3Smrg      return;
10157ec681f3Smrg   }
10167ec681f3Smrg
10177ec681f3Smrg   if (!is_time_query(query) && !is_bool_query(query)) {
10187ec681f3Smrg      if (num_queries == 1 && query->type != PIPE_QUERY_PRIMITIVES_GENERATED &&
10197ec681f3Smrg                              query->type != PIPE_QUERY_PRIMITIVES_EMITTED &&
10207ec681f3Smrg                              !is_bool_query(query)) {
10217ec681f3Smrg         if (size_flags == VK_QUERY_RESULT_64_BIT) {
10227ec681f3Smrg            if (query->needs_update)
10237ec681f3Smrg               update_qbo(ctx, query);
10247ec681f3Smrg            /* internal qbo always writes 64bit value so we can just direct copy */
10257ec681f3Smrg            zink_copy_buffer(ctx, res, zink_resource(query->curr_qbo->buffer), offset,
10267ec681f3Smrg                             get_buffer_offset(query, query->curr_qbo->buffer, query->last_start),
10277ec681f3Smrg                             result_size);
10287ec681f3Smrg         } else
10297ec681f3Smrg            /* have to do a new copy for 32bit */
10307ec681f3Smrg            copy_results_to_buffer(ctx, query, res, offset, 1, size_flags);
10317ec681f3Smrg         return;
10327ec681f3Smrg      }
10337ec681f3Smrg   }
10347ec681f3Smrg
10357ec681f3Smrg   /* TODO: use CS to aggregate results */
10367ec681f3Smrg
10377ec681f3Smrg   /* unfortunately, there's no way to accumulate results from multiple queries on the gpu without either
10387ec681f3Smrg    * clobbering all but the last result or writing the results sequentially, so we have to manually write the result
10397ec681f3Smrg    */
10407ec681f3Smrg   force_cpu_read(ctx, pquery, result_type, pres, offset);
10417ec681f3Smrg}
10427ec681f3Smrg
10437ec681f3Smrgstatic uint64_t
10447ec681f3Smrgzink_get_timestamp(struct pipe_context *pctx)
10457ec681f3Smrg{
10467ec681f3Smrg   struct zink_screen *screen = zink_screen(pctx->screen);
10477ec681f3Smrg   uint64_t timestamp, deviation;
10487ec681f3Smrg   assert(screen->info.have_EXT_calibrated_timestamps);
10497ec681f3Smrg   VkCalibratedTimestampInfoEXT cti = {0};
10507ec681f3Smrg   cti.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
10517ec681f3Smrg   cti.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
10527ec681f3Smrg   VKSCR(GetCalibratedTimestampsEXT)(screen->dev, 1, &cti, &timestamp, &deviation);
10537ec681f3Smrg   timestamp_to_nanoseconds(screen, &timestamp);
10547ec681f3Smrg   return timestamp;
10557ec681f3Smrg}
10567ec681f3Smrg
10577ec681f3Smrgvoid
10587ec681f3Smrgzink_context_query_init(struct pipe_context *pctx)
10597ec681f3Smrg{
10607ec681f3Smrg   struct zink_context *ctx = zink_context(pctx);
10617ec681f3Smrg   list_inithead(&ctx->suspended_queries);
10627ec681f3Smrg   list_inithead(&ctx->primitives_generated_queries);
10637ec681f3Smrg
10647ec681f3Smrg   pctx->create_query = zink_create_query;
10657ec681f3Smrg   pctx->destroy_query = zink_destroy_query;
10667ec681f3Smrg   pctx->begin_query = zink_begin_query;
10677ec681f3Smrg   pctx->end_query = zink_end_query;
10687ec681f3Smrg   pctx->get_query_result = zink_get_query_result;
10697ec681f3Smrg   pctx->get_query_result_resource = zink_get_query_result_resource;
10707ec681f3Smrg   pctx->set_active_query_state = zink_set_active_query_state;
10717ec681f3Smrg   pctx->render_condition = zink_render_condition;
10727ec681f3Smrg   pctx->get_timestamp = zink_get_timestamp;
10737ec681f3Smrg}
1074