xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/virgl/virgl_query.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2014, 2015 Red Hat.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "util/u_memory.h"
25 #include "util/u_inlines.h"
26 #include "virgl_context.h"
27 #include "virgl_encode.h"
28 #include "virtio-gpu/virgl_protocol.h"
29 #include "virgl_resource.h"
30 #include "virgl_screen.h"
31 
32 struct virgl_query {
33    enum pipe_query_type type;
34 
35    union {
36       struct virgl_resource *buf;
37       struct pipe_fence_handle *fence; // PIPE_QUERY_GPU_FINISHED
38    };
39 
40    uint32_t handle;
41    uint32_t result_size;
42    uint32_t pipeline_stats;
43 
44    bool ready;
45    uint64_t result;
46 };
47 
48 #define VIRGL_QUERY_OCCLUSION_COUNTER     0
49 #define VIRGL_QUERY_OCCLUSION_PREDICATE   1
50 #define VIRGL_QUERY_TIMESTAMP             2
51 #define VIRGL_QUERY_TIMESTAMP_DISJOINT    3
52 #define VIRGL_QUERY_TIME_ELAPSED          4
53 #define VIRGL_QUERY_PRIMITIVES_GENERATED  5
54 #define VIRGL_QUERY_PRIMITIVES_EMITTED    6
55 #define VIRGL_QUERY_SO_STATISTICS         7
56 #define VIRGL_QUERY_SO_OVERFLOW_PREDICATE 8
57 #define VIRGL_QUERY_GPU_FINISHED          9
58 #define VIRGL_QUERY_PIPELINE_STATISTICS  10
59 #define VIRGL_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE 11
60 #define VIRGL_QUERY_SO_OVERFLOW_ANY_PREDICATE 12
61 
62 static const int pquery_map[] =
63 {
64    VIRGL_QUERY_OCCLUSION_COUNTER,
65    VIRGL_QUERY_OCCLUSION_PREDICATE,
66    VIRGL_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE,
67    VIRGL_QUERY_TIMESTAMP,
68    VIRGL_QUERY_TIMESTAMP_DISJOINT,
69    VIRGL_QUERY_TIME_ELAPSED,
70    VIRGL_QUERY_PRIMITIVES_GENERATED,
71    VIRGL_QUERY_PRIMITIVES_EMITTED,
72    VIRGL_QUERY_SO_STATISTICS,
73    VIRGL_QUERY_SO_OVERFLOW_PREDICATE,
74    VIRGL_QUERY_SO_OVERFLOW_ANY_PREDICATE,
75    VIRGL_QUERY_GPU_FINISHED,
76    VIRGL_QUERY_PIPELINE_STATISTICS,
77 };
78 
pipe_to_virgl_query(enum pipe_query_type ptype)79 static int pipe_to_virgl_query(enum pipe_query_type ptype)
80 {
81    return pquery_map[ptype];
82 }
83 
84 static const enum virgl_statistics_query_index stats_index_map[] = {
85    [PIPE_STAT_QUERY_IA_VERTICES] = VIRGL_STAT_QUERY_IA_VERTICES,
86    [PIPE_STAT_QUERY_IA_PRIMITIVES] = VIRGL_STAT_QUERY_IA_PRIMITIVES,
87    [PIPE_STAT_QUERY_VS_INVOCATIONS] = VIRGL_STAT_QUERY_VS_INVOCATIONS,
88    [PIPE_STAT_QUERY_GS_INVOCATIONS] = VIRGL_STAT_QUERY_GS_INVOCATIONS,
89    [PIPE_STAT_QUERY_GS_PRIMITIVES] = VIRGL_STAT_QUERY_GS_PRIMITIVES,
90    [PIPE_STAT_QUERY_C_INVOCATIONS] = VIRGL_STAT_QUERY_C_INVOCATIONS,
91    [PIPE_STAT_QUERY_C_PRIMITIVES] = VIRGL_STAT_QUERY_C_PRIMITIVES,
92    [PIPE_STAT_QUERY_PS_INVOCATIONS] = VIRGL_STAT_QUERY_PS_INVOCATIONS,
93    [PIPE_STAT_QUERY_HS_INVOCATIONS] = VIRGL_STAT_QUERY_HS_INVOCATIONS,
94    [PIPE_STAT_QUERY_DS_INVOCATIONS] = VIRGL_STAT_QUERY_DS_INVOCATIONS,
95    [PIPE_STAT_QUERY_CS_INVOCATIONS] = VIRGL_STAT_QUERY_CS_INVOCATIONS,
96 };
97 
98 static enum virgl_statistics_query_index
pipe_stats_query_to_virgl(enum pipe_statistics_query_index index)99 pipe_stats_query_to_virgl(enum pipe_statistics_query_index index)
100 {
101    return stats_index_map[index];
102 }
103 
virgl_query(struct pipe_query * q)104 static inline struct virgl_query *virgl_query(struct pipe_query *q)
105 {
106    return (struct virgl_query *)q;
107 }
108 
virgl_render_condition(struct pipe_context * ctx,struct pipe_query * q,bool condition,enum pipe_render_cond_flag mode)109 static void virgl_render_condition(struct pipe_context *ctx,
110                                   struct pipe_query *q,
111                                   bool condition,
112                                   enum pipe_render_cond_flag mode)
113 {
114    struct virgl_context *vctx = virgl_context(ctx);
115    struct virgl_query *query = virgl_query(q);
116    uint32_t handle = 0;
117    if (q)
118       handle = query->handle;
119    virgl_encoder_render_condition(vctx, handle, condition, mode);
120 }
121 
virgl_create_query(struct pipe_context * ctx,unsigned query_type,unsigned index)122 static struct pipe_query *virgl_create_query(struct pipe_context *ctx,
123                                             unsigned query_type, unsigned index)
124 {
125    struct virgl_context *vctx = virgl_context(ctx);
126    struct virgl_query *query;
127 
128    query = CALLOC_STRUCT(virgl_query);
129    if (!query)
130       return NULL;
131 
132    query->type = query_type;
133 
134    if (query->type == PIPE_QUERY_GPU_FINISHED)
135       return (struct pipe_query *)query;
136 
137    query->buf = (struct virgl_resource *)
138       pipe_buffer_create(ctx->screen, PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
139                          sizeof(struct virgl_host_query_state));
140    if (!query->buf) {
141       FREE(query);
142       return NULL;
143    }
144 
145    query->handle = virgl_object_assign_handle();
146    query->result_size = (query_type == PIPE_QUERY_TIMESTAMP ||
147                          query_type == PIPE_QUERY_TIME_ELAPSED) ? 8 : 4;
148 
149    if (query_type == PIPE_QUERY_PIPELINE_STATISTICS) {
150       query->pipeline_stats = index;
151 
152       index = pipe_stats_query_to_virgl(index);
153    } else {
154       query->pipeline_stats = ~0;
155    }
156 
157    util_range_add(&query->buf->b, &query->buf->valid_buffer_range, 0,
158                   sizeof(struct virgl_host_query_state));
159    virgl_resource_dirty(query->buf, 0);
160 
161    virgl_encoder_create_query(vctx, query->handle,
162          pipe_to_virgl_query(query_type), index, query->buf, 0);
163 
164    return (struct pipe_query *)query;
165 }
166 
virgl_destroy_query(struct pipe_context * ctx,struct pipe_query * q)167 static void virgl_destroy_query(struct pipe_context *ctx,
168                         struct pipe_query *q)
169 {
170    struct virgl_context *vctx = virgl_context(ctx);
171    struct virgl_query *query = virgl_query(q);
172 
173    if (query->type == PIPE_QUERY_GPU_FINISHED) {
174       ctx->screen->fence_reference(ctx->screen, &query->fence, NULL);
175    } else {
176       virgl_encode_delete_object(vctx, query->handle, VIRGL_OBJECT_QUERY);
177       pipe_resource_reference((struct pipe_resource **)&query->buf, NULL);
178    }
179 
180    FREE(query);
181 }
182 
virgl_begin_query(struct pipe_context * ctx,struct pipe_query * q)183 static bool virgl_begin_query(struct pipe_context *ctx,
184                              struct pipe_query *q)
185 {
186    struct virgl_context *vctx = virgl_context(ctx);
187    struct virgl_query *query = virgl_query(q);
188 
189    virgl_encoder_begin_query(vctx, query->handle);
190 
191    return true;
192 }
193 
virgl_end_query(struct pipe_context * ctx,struct pipe_query * q)194 static bool virgl_end_query(struct pipe_context *ctx,
195                            struct pipe_query *q)
196 {
197    struct virgl_screen *vs = virgl_screen(ctx->screen);
198    struct virgl_context *vctx = virgl_context(ctx);
199    struct virgl_query *query = virgl_query(q);
200    struct virgl_host_query_state *host_state;
201 
202    if (query->type == PIPE_QUERY_GPU_FINISHED) {
203       ctx->flush(ctx, &query->fence, PIPE_FLUSH_DEFERRED);
204       return true;
205    }
206 
207    host_state = vs->vws->resource_map(vs->vws, query->buf->hw_res);
208    if (!host_state)
209       return false;
210 
211    host_state->query_state = VIRGL_QUERY_STATE_WAIT_HOST;
212    query->ready = false;
213 
214    virgl_encoder_end_query(vctx, query->handle);
215 
216    /* start polling now */
217    virgl_encoder_get_query_result(vctx, query->handle, 0);
218    vs->vws->emit_res(vs->vws, vctx->cbuf, query->buf->hw_res, false);
219 
220    return true;
221 }
222 
virgl_get_query_result(struct pipe_context * ctx,struct pipe_query * q,bool wait,union pipe_query_result * result)223 static bool virgl_get_query_result(struct pipe_context *ctx,
224                                    struct pipe_query *q,
225                                    bool wait,
226                                    union pipe_query_result *result)
227 {
228    struct virgl_query *query = virgl_query(q);
229 
230    if (query->type == PIPE_QUERY_GPU_FINISHED) {
231       struct pipe_screen *screen = ctx->screen;
232 
233       result->b = screen->fence_finish(screen, ctx, query->fence, wait ? OS_TIMEOUT_INFINITE : 0);
234       return result->b;
235    }
236 
237    if (!query->ready) {
238       struct virgl_screen *vs = virgl_screen(ctx->screen);
239       struct virgl_context *vctx = virgl_context(ctx);
240       volatile struct virgl_host_query_state *host_state;
241       struct pipe_transfer *transfer = NULL;
242 
243       if (vs->vws->res_is_referenced(vs->vws, vctx->cbuf, query->buf->hw_res))
244          ctx->flush(ctx, NULL, 0);
245 
246       if (wait)
247          vs->vws->resource_wait(vs->vws, query->buf->hw_res);
248       else if (vs->vws->resource_is_busy(vs->vws, query->buf->hw_res))
249          return false;
250 
251       host_state = vs->vws->resource_map(vs->vws, query->buf->hw_res);
252 
253       /* The resource is idle and the result should be available at this point,
254        * unless we are dealing with an older host.  In that case,
255        * VIRGL_CCMD_GET_QUERY_RESULT is not fenced, the buffer is not
256        * coherent, and transfers are unsynchronized.  We have to repeatedly
257        * transfer until we get the result back.
258        */
259       while (host_state->query_state != VIRGL_QUERY_STATE_DONE) {
260          debug_printf("VIRGL: get_query_result is forced blocking\n");
261 
262          if (transfer) {
263             pipe_buffer_unmap(ctx, transfer);
264             if (!wait)
265                return false;
266          }
267 
268          host_state = pipe_buffer_map(ctx, &query->buf->b,
269                PIPE_MAP_READ, &transfer);
270       }
271 
272       if (query->result_size == 8)
273          query->result = host_state->result;
274       else
275          query->result = (uint32_t) host_state->result;
276 
277       if (transfer)
278          pipe_buffer_unmap(ctx, transfer);
279 
280       query->ready = true;
281    }
282 
283    switch (query->pipeline_stats) {
284    case PIPE_STAT_QUERY_IA_VERTICES: result->pipeline_statistics.ia_vertices = query->result; break;
285    case PIPE_STAT_QUERY_IA_PRIMITIVES: result->pipeline_statistics.ia_primitives = query->result; break;
286    case PIPE_STAT_QUERY_VS_INVOCATIONS: result->pipeline_statistics.vs_invocations = query->result; break;
287    case PIPE_STAT_QUERY_GS_INVOCATIONS: result->pipeline_statistics.gs_invocations = query->result; break;
288    case PIPE_STAT_QUERY_GS_PRIMITIVES: result->pipeline_statistics.gs_primitives = query->result; break;
289    case PIPE_STAT_QUERY_PS_INVOCATIONS: result->pipeline_statistics.ps_invocations = query->result; break;
290    case PIPE_STAT_QUERY_HS_INVOCATIONS: result->pipeline_statistics.hs_invocations = query->result; break;
291    case PIPE_STAT_QUERY_CS_INVOCATIONS: result->pipeline_statistics.cs_invocations = query->result; break;
292    case PIPE_STAT_QUERY_C_INVOCATIONS: result->pipeline_statistics.c_invocations = query->result; break;
293    case PIPE_STAT_QUERY_C_PRIMITIVES: result->pipeline_statistics.c_primitives = query->result; break;
294    case PIPE_STAT_QUERY_DS_INVOCATIONS: result->pipeline_statistics.ds_invocations = query->result; break;
295    default:
296       result->u64 = query->result;
297    }
298 
299    return true;
300 }
301 
302 static void
virgl_set_active_query_state(struct pipe_context * pipe,bool enable)303 virgl_set_active_query_state(struct pipe_context *pipe, bool enable)
304 {
305 }
306 
307 static void
virgl_get_query_result_resource(struct pipe_context * ctx,struct pipe_query * q,enum pipe_query_flags flags,enum pipe_query_value_type result_type,int index,struct pipe_resource * resource,unsigned offset)308 virgl_get_query_result_resource(struct pipe_context *ctx,
309                                 struct pipe_query *q,
310                                 enum pipe_query_flags flags,
311                                 enum pipe_query_value_type result_type,
312                                 int index,
313                                 struct pipe_resource *resource,
314                                 unsigned offset)
315 {
316    struct virgl_context *vctx = virgl_context(ctx);
317    struct virgl_query *query = virgl_query(q);
318    struct virgl_resource *qbo = (struct virgl_resource *)resource;
319 
320    virgl_resource_dirty(qbo, 0);
321    virgl_encode_get_query_result_qbo(vctx, query->handle, qbo, (flags & PIPE_QUERY_WAIT), result_type, offset, index);
322 }
323 
virgl_init_query_functions(struct virgl_context * vctx)324 void virgl_init_query_functions(struct virgl_context *vctx)
325 {
326    vctx->base.render_condition = virgl_render_condition;
327    vctx->base.create_query = virgl_create_query;
328    vctx->base.destroy_query = virgl_destroy_query;
329    vctx->base.begin_query = virgl_begin_query;
330    vctx->base.end_query = virgl_end_query;
331    vctx->base.get_query_result = virgl_get_query_result;
332    vctx->base.set_active_query_state = virgl_set_active_query_state;
333    vctx->base.get_query_result_resource = virgl_get_query_result_resource;
334 }
335