1 /**************************************************************************
2  *
3  * Copyright 2007 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 
29 /**
30  * glBegin/EndQuery interface to pipe
31  *
32  * \author Brian Paul
33  */
34 
35 
36 #include "util/compiler.h"
37 #include "main/context.h"
38 #include "main/queryobj.h"
39 
40 #include "pipe/p_context.h"
41 #include "pipe/p_defines.h"
42 #include "pipe/p_screen.h"
43 #include "util/u_inlines.h"
44 #include "st_context.h"
45 #include "st_cb_queryobj.h"
46 #include "st_cb_bitmap.h"
47 #include "st_cb_bufferobjects.h"
48 #include "st_util.h"
49 
50 
51 static struct gl_query_object *
st_NewQueryObject(struct gl_context * ctx,GLuint id)52 st_NewQueryObject(struct gl_context *ctx, GLuint id)
53 {
54    struct st_query_object *stq = ST_CALLOC_STRUCT(st_query_object);
55    if (stq) {
56       stq->base.Id = id;
57       stq->base.Ready = GL_TRUE;
58       stq->pq = NULL;
59       stq->type = PIPE_QUERY_TYPES; /* an invalid value */
60       return &stq->base;
61    }
62    return NULL;
63 }
64 
65 
66 static void
free_queries(struct pipe_context * pipe,struct st_query_object * stq)67 free_queries(struct pipe_context *pipe, struct st_query_object *stq)
68 {
69    if (stq->pq) {
70       pipe->destroy_query(pipe, stq->pq);
71       stq->pq = NULL;
72    }
73 
74    if (stq->pq_begin) {
75       pipe->destroy_query(pipe, stq->pq_begin);
76       stq->pq_begin = NULL;
77    }
78 }
79 
80 
81 static void
st_DeleteQuery(struct gl_context * ctx,struct gl_query_object * q)82 st_DeleteQuery(struct gl_context *ctx, struct gl_query_object *q)
83 {
84    struct pipe_context *pipe = st_context(ctx)->pipe;
85    struct st_query_object *stq = st_query_object(q);
86 
87    free_queries(pipe, stq);
88 
89    _mesa_delete_query(ctx, q);
90 }
91 
92 static int
target_to_index(const struct st_context * st,const struct gl_query_object * q)93 target_to_index(const struct st_context *st, const struct gl_query_object *q)
94 {
95    if (q->Target == GL_PRIMITIVES_GENERATED ||
96        q->Target == GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN ||
97        q->Target == GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB)
98       return q->Stream;
99 
100    if (st->has_single_pipe_stat) {
101       switch (q->Target) {
102       case GL_VERTICES_SUBMITTED_ARB:
103          return PIPE_STAT_QUERY_IA_VERTICES;
104       case GL_PRIMITIVES_SUBMITTED_ARB:
105          return PIPE_STAT_QUERY_IA_PRIMITIVES;
106       case GL_VERTEX_SHADER_INVOCATIONS_ARB:
107          return PIPE_STAT_QUERY_VS_INVOCATIONS;
108       case GL_GEOMETRY_SHADER_INVOCATIONS:
109          return PIPE_STAT_QUERY_GS_INVOCATIONS;
110       case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
111          return PIPE_STAT_QUERY_GS_PRIMITIVES;
112       case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
113          return PIPE_STAT_QUERY_C_INVOCATIONS;
114       case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
115          return PIPE_STAT_QUERY_C_PRIMITIVES;
116       case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
117          return PIPE_STAT_QUERY_PS_INVOCATIONS;
118       case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
119          return PIPE_STAT_QUERY_HS_INVOCATIONS;
120       case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
121          return PIPE_STAT_QUERY_DS_INVOCATIONS;
122       case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
123          return PIPE_STAT_QUERY_CS_INVOCATIONS;
124       default:
125          break;
126       }
127    }
128 
129    return 0;
130 }
131 
132 static void
st_BeginQuery(struct gl_context * ctx,struct gl_query_object * q)133 st_BeginQuery(struct gl_context *ctx, struct gl_query_object *q)
134 {
135    struct st_context *st = st_context(ctx);
136    struct pipe_context *pipe = st->pipe;
137    struct st_query_object *stq = st_query_object(q);
138    unsigned type;
139    bool ret = false;
140 
141    st_flush_bitmap_cache(st_context(ctx));
142 
143    /* convert GL query type to Gallium query type */
144    switch (q->Target) {
145    case GL_ANY_SAMPLES_PASSED:
146       type = PIPE_QUERY_OCCLUSION_PREDICATE;
147       break;
148    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
149       type = PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE;
150       break;
151    case GL_SAMPLES_PASSED_ARB:
152       type = PIPE_QUERY_OCCLUSION_COUNTER;
153       break;
154    case GL_PRIMITIVES_GENERATED:
155       type = PIPE_QUERY_PRIMITIVES_GENERATED;
156       break;
157    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
158       type = PIPE_QUERY_PRIMITIVES_EMITTED;
159       break;
160    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
161       type = PIPE_QUERY_SO_OVERFLOW_PREDICATE;
162       break;
163    case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
164       type = PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE;
165       break;
166    case GL_TIME_ELAPSED:
167       if (st->has_time_elapsed)
168          type = PIPE_QUERY_TIME_ELAPSED;
169       else
170          type = PIPE_QUERY_TIMESTAMP;
171       break;
172    case GL_VERTICES_SUBMITTED_ARB:
173    case GL_PRIMITIVES_SUBMITTED_ARB:
174    case GL_VERTEX_SHADER_INVOCATIONS_ARB:
175    case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
176    case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
177    case GL_GEOMETRY_SHADER_INVOCATIONS:
178    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
179    case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
180    case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
181    case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
182    case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
183       type = st->has_single_pipe_stat ? PIPE_QUERY_PIPELINE_STATISTICS_SINGLE
184                                       : PIPE_QUERY_PIPELINE_STATISTICS;
185       break;
186    default:
187       assert(0 && "unexpected query target in st_BeginQuery()");
188       return;
189    }
190 
191    if (stq->type != type) {
192       /* free old query of different type */
193       free_queries(pipe, stq);
194       stq->type = PIPE_QUERY_TYPES; /* an invalid value */
195    }
196 
197    if (q->Target == GL_TIME_ELAPSED &&
198        type == PIPE_QUERY_TIMESTAMP) {
199       /* Determine time elapsed by emitting two timestamp queries. */
200       if (!stq->pq_begin) {
201          stq->pq_begin = pipe->create_query(pipe, type, 0);
202          stq->type = type;
203       }
204       if (stq->pq_begin)
205          ret = pipe->end_query(pipe, stq->pq_begin);
206    } else {
207       if (!stq->pq) {
208          stq->pq = pipe->create_query(pipe, type, target_to_index(st, q));
209          stq->type = type;
210       }
211       if (stq->pq)
212          ret = pipe->begin_query(pipe, stq->pq);
213    }
214 
215    if (!ret) {
216       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery");
217 
218       free_queries(pipe, stq);
219       q->Active = GL_FALSE;
220       return;
221    }
222 
223    if (stq->type != PIPE_QUERY_TIMESTAMP)
224       st->active_queries++;
225 
226    assert(stq->type == type);
227 }
228 
229 
230 static void
st_EndQuery(struct gl_context * ctx,struct gl_query_object * q)231 st_EndQuery(struct gl_context *ctx, struct gl_query_object *q)
232 {
233    struct st_context *st = st_context(ctx);
234    struct pipe_context *pipe = st->pipe;
235    struct st_query_object *stq = st_query_object(q);
236    bool ret = false;
237 
238    st_flush_bitmap_cache(st_context(ctx));
239 
240    if ((q->Target == GL_TIMESTAMP ||
241         q->Target == GL_TIME_ELAPSED) &&
242        !stq->pq) {
243       stq->pq = pipe->create_query(pipe, PIPE_QUERY_TIMESTAMP, 0);
244       stq->type = PIPE_QUERY_TIMESTAMP;
245    }
246 
247    if (stq->pq)
248       ret = pipe->end_query(pipe, stq->pq);
249 
250    if (!ret) {
251       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glEndQuery");
252       return;
253    }
254 
255    if (stq->type != PIPE_QUERY_TIMESTAMP)
256       st->active_queries--;
257 }
258 
259 
260 static boolean
get_query_result(struct pipe_context * pipe,struct st_query_object * stq,boolean wait)261 get_query_result(struct pipe_context *pipe,
262                  struct st_query_object *stq,
263                  boolean wait)
264 {
265    union pipe_query_result data;
266 
267    if (!stq->pq) {
268       /* Only needed in case we failed to allocate the gallium query earlier.
269        * Return TRUE so we don't spin on this forever.
270        */
271       return TRUE;
272    }
273 
274    if (!pipe->get_query_result(pipe, stq->pq, wait, &data))
275       return FALSE;
276 
277    switch (stq->type) {
278    case PIPE_QUERY_PIPELINE_STATISTICS:
279       switch (stq->base.Target) {
280       case GL_VERTICES_SUBMITTED_ARB:
281          stq->base.Result = data.pipeline_statistics.ia_vertices;
282          break;
283       case GL_PRIMITIVES_SUBMITTED_ARB:
284          stq->base.Result = data.pipeline_statistics.ia_primitives;
285          break;
286       case GL_VERTEX_SHADER_INVOCATIONS_ARB:
287          stq->base.Result = data.pipeline_statistics.vs_invocations;
288          break;
289       case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
290          stq->base.Result = data.pipeline_statistics.hs_invocations;
291          break;
292       case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
293          stq->base.Result = data.pipeline_statistics.ds_invocations;
294          break;
295       case GL_GEOMETRY_SHADER_INVOCATIONS:
296          stq->base.Result = data.pipeline_statistics.gs_invocations;
297          break;
298       case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
299          stq->base.Result = data.pipeline_statistics.gs_primitives;
300          break;
301       case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
302          stq->base.Result = data.pipeline_statistics.ps_invocations;
303          break;
304       case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
305          stq->base.Result = data.pipeline_statistics.cs_invocations;
306          break;
307       case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
308          stq->base.Result = data.pipeline_statistics.c_invocations;
309          break;
310       case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
311          stq->base.Result = data.pipeline_statistics.c_primitives;
312          break;
313       default:
314          unreachable("invalid pipeline statistics counter");
315       }
316       break;
317    case PIPE_QUERY_OCCLUSION_PREDICATE:
318    case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
319    case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
320    case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
321       stq->base.Result = !!data.b;
322       break;
323    default:
324       stq->base.Result = data.u64;
325       break;
326    }
327 
328    if (stq->base.Target == GL_TIME_ELAPSED &&
329        stq->type == PIPE_QUERY_TIMESTAMP) {
330       /* Calculate the elapsed time from the two timestamp queries */
331       GLuint64EXT Result0 = 0;
332       assert(stq->pq_begin);
333       pipe->get_query_result(pipe, stq->pq_begin, TRUE, (void *)&Result0);
334       stq->base.Result -= Result0;
335    } else {
336       assert(!stq->pq_begin);
337    }
338 
339    return TRUE;
340 }
341 
342 
343 static void
st_WaitQuery(struct gl_context * ctx,struct gl_query_object * q)344 st_WaitQuery(struct gl_context *ctx, struct gl_query_object *q)
345 {
346    struct pipe_context *pipe = st_context(ctx)->pipe;
347    struct st_query_object *stq = st_query_object(q);
348 
349    /* this function should only be called if we don't have a ready result */
350    assert(!stq->base.Ready);
351 
352    while (!stq->base.Ready &&
353 	  !get_query_result(pipe, stq, TRUE))
354    {
355       /* nothing */
356    }
357 
358    q->Ready = GL_TRUE;
359 }
360 
361 
362 static void
st_CheckQuery(struct gl_context * ctx,struct gl_query_object * q)363 st_CheckQuery(struct gl_context *ctx, struct gl_query_object *q)
364 {
365    struct pipe_context *pipe = st_context(ctx)->pipe;
366    struct st_query_object *stq = st_query_object(q);
367    assert(!q->Ready);   /* we should not get called if Ready is TRUE */
368    q->Ready = get_query_result(pipe, stq, FALSE);
369 }
370 
371 
372 static uint64_t
st_GetTimestamp(struct gl_context * ctx)373 st_GetTimestamp(struct gl_context *ctx)
374 {
375    struct pipe_context *pipe = st_context(ctx)->pipe;
376    struct pipe_screen *screen = st_context(ctx)->screen;
377 
378    /* Prefer the per-screen function */
379    if (screen->get_timestamp) {
380       return screen->get_timestamp(screen);
381    }
382    else {
383       /* Fall back to the per-context function */
384       assert(pipe->get_timestamp);
385       return pipe->get_timestamp(pipe);
386    }
387 }
388 
389 static void
st_StoreQueryResult(struct gl_context * ctx,struct gl_query_object * q,struct gl_buffer_object * buf,intptr_t offset,GLenum pname,GLenum ptype)390 st_StoreQueryResult(struct gl_context *ctx, struct gl_query_object *q,
391                     struct gl_buffer_object *buf, intptr_t offset,
392                     GLenum pname, GLenum ptype)
393 {
394    struct pipe_context *pipe = st_context(ctx)->pipe;
395    struct st_query_object *stq = st_query_object(q);
396    struct st_buffer_object *stObj = st_buffer_object(buf);
397    boolean wait = pname == GL_QUERY_RESULT;
398    enum pipe_query_value_type result_type;
399    int index;
400 
401    /* GL_QUERY_TARGET is a bit of an extension since it has nothing to
402     * do with the GPU end of the query. Write it in "by hand".
403     */
404    if (pname == GL_QUERY_TARGET) {
405       /* Assume that the data must be LE. The endianness situation wrt CPU and
406        * GPU is incredibly confusing, but the vast majority of GPUs are
407        * LE. When a BE one comes along, this needs some form of resolution.
408        */
409       unsigned data[2] = { CPU_TO_LE32(q->Target), 0 };
410       pipe_buffer_write(pipe, stObj->buffer, offset,
411                         (ptype == GL_INT64_ARB ||
412                          ptype == GL_UNSIGNED_INT64_ARB) ? 8 : 4,
413                         data);
414       return;
415    }
416 
417    switch (ptype) {
418    case GL_INT:
419       result_type = PIPE_QUERY_TYPE_I32;
420       break;
421    case GL_UNSIGNED_INT:
422       result_type = PIPE_QUERY_TYPE_U32;
423       break;
424    case GL_INT64_ARB:
425       result_type = PIPE_QUERY_TYPE_I64;
426       break;
427    case GL_UNSIGNED_INT64_ARB:
428       result_type = PIPE_QUERY_TYPE_U64;
429       break;
430    default:
431       unreachable("Unexpected result type");
432    }
433 
434    if (pname == GL_QUERY_RESULT_AVAILABLE) {
435       index = -1;
436    } else if (stq->type == PIPE_QUERY_PIPELINE_STATISTICS) {
437       switch (q->Target) {
438       case GL_VERTICES_SUBMITTED_ARB:
439          index = PIPE_STAT_QUERY_IA_VERTICES;
440          break;
441       case GL_PRIMITIVES_SUBMITTED_ARB:
442          index = PIPE_STAT_QUERY_IA_PRIMITIVES;
443          break;
444       case GL_VERTEX_SHADER_INVOCATIONS_ARB:
445          index = PIPE_STAT_QUERY_VS_INVOCATIONS;
446          break;
447       case GL_GEOMETRY_SHADER_INVOCATIONS:
448          index = PIPE_STAT_QUERY_GS_INVOCATIONS;
449          break;
450       case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
451          index = PIPE_STAT_QUERY_GS_PRIMITIVES;
452          break;
453       case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
454          index = PIPE_STAT_QUERY_C_INVOCATIONS;
455          break;
456       case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
457          index = PIPE_STAT_QUERY_C_PRIMITIVES;
458          break;
459       case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
460          index = PIPE_STAT_QUERY_PS_INVOCATIONS;
461          break;
462       case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
463          index = PIPE_STAT_QUERY_HS_INVOCATIONS;
464          break;
465       case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
466          index = PIPE_STAT_QUERY_DS_INVOCATIONS;
467          break;
468       case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
469          index = PIPE_STAT_QUERY_CS_INVOCATIONS;
470          break;
471       default:
472          unreachable("Unexpected target");
473       }
474    } else {
475       index = 0;
476    }
477 
478    pipe->get_query_result_resource(pipe, stq->pq, wait, result_type, index,
479                                    stObj->buffer, offset);
480 }
481 
st_init_query_functions(struct dd_function_table * functions)482 void st_init_query_functions(struct dd_function_table *functions)
483 {
484    functions->NewQueryObject = st_NewQueryObject;
485    functions->DeleteQuery = st_DeleteQuery;
486    functions->BeginQuery = st_BeginQuery;
487    functions->EndQuery = st_EndQuery;
488    functions->WaitQuery = st_WaitQuery;
489    functions->CheckQuery = st_CheckQuery;
490    functions->GetTimestamp = st_GetTimestamp;
491    functions->StoreQueryResult = st_StoreQueryResult;
492 }
493