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