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 "main/imports.h"
37 #include "main/compiler.h"
38 #include "main/context.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 
49 
50 static struct gl_query_object *
st_NewQueryObject(struct gl_context * ctx,GLuint id)51 st_NewQueryObject(struct gl_context *ctx, GLuint id)
52 {
53    struct st_query_object *stq = ST_CALLOC_STRUCT(st_query_object);
54    if (stq) {
55       stq->base.Id = id;
56       stq->base.Ready = GL_TRUE;
57       stq->pq = NULL;
58       stq->type = PIPE_QUERY_TYPES; /* an invalid value */
59       return &stq->base;
60    }
61    return NULL;
62 }
63 
64 
65 static void
free_queries(struct pipe_context * pipe,struct st_query_object * stq)66 free_queries(struct pipe_context *pipe, struct st_query_object *stq)
67 {
68    if (stq->pq) {
69       pipe->destroy_query(pipe, stq->pq);
70       stq->pq = NULL;
71    }
72 
73    if (stq->pq_begin) {
74       pipe->destroy_query(pipe, stq->pq_begin);
75       stq->pq_begin = NULL;
76    }
77 }
78 
79 
80 static void
st_DeleteQuery(struct gl_context * ctx,struct gl_query_object * q)81 st_DeleteQuery(struct gl_context *ctx, struct gl_query_object *q)
82 {
83    struct pipe_context *pipe = st_context(ctx)->pipe;
84    struct st_query_object *stq = st_query_object(q);
85 
86    free_queries(pipe, stq);
87 
88    free(stq);
89 }
90 
91 
92 static void
st_BeginQuery(struct gl_context * ctx,struct gl_query_object * q)93 st_BeginQuery(struct gl_context *ctx, struct gl_query_object *q)
94 {
95    struct st_context *st = st_context(ctx);
96    struct pipe_context *pipe = st->pipe;
97    struct st_query_object *stq = st_query_object(q);
98    unsigned type;
99    bool ret = false;
100 
101    st_flush_bitmap_cache(st_context(ctx));
102 
103    /* convert GL query type to Gallium query type */
104    switch (q->Target) {
105    case GL_ANY_SAMPLES_PASSED:
106       type = PIPE_QUERY_OCCLUSION_PREDICATE;
107       break;
108    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
109       type = PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE;
110       break;
111    case GL_SAMPLES_PASSED_ARB:
112       type = PIPE_QUERY_OCCLUSION_COUNTER;
113       break;
114    case GL_PRIMITIVES_GENERATED:
115       type = PIPE_QUERY_PRIMITIVES_GENERATED;
116       break;
117    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
118       type = PIPE_QUERY_PRIMITIVES_EMITTED;
119       break;
120    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
121       type = PIPE_QUERY_SO_OVERFLOW_PREDICATE;
122       break;
123    case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
124       type = PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE;
125       break;
126    case GL_TIME_ELAPSED:
127       if (st->has_time_elapsed)
128          type = PIPE_QUERY_TIME_ELAPSED;
129       else
130          type = PIPE_QUERY_TIMESTAMP;
131       break;
132    case GL_VERTICES_SUBMITTED_ARB:
133    case GL_PRIMITIVES_SUBMITTED_ARB:
134    case GL_VERTEX_SHADER_INVOCATIONS_ARB:
135    case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
136    case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
137    case GL_GEOMETRY_SHADER_INVOCATIONS:
138    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
139    case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
140    case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
141    case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
142    case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
143       type = PIPE_QUERY_PIPELINE_STATISTICS;
144       break;
145    default:
146       assert(0 && "unexpected query target in st_BeginQuery()");
147       return;
148    }
149 
150    if (stq->type != type) {
151       /* free old query of different type */
152       free_queries(pipe, stq);
153       stq->type = PIPE_QUERY_TYPES; /* an invalid value */
154    }
155 
156    if (q->Target == GL_TIME_ELAPSED &&
157        type == PIPE_QUERY_TIMESTAMP) {
158       /* Determine time elapsed by emitting two timestamp queries. */
159       if (!stq->pq_begin) {
160          stq->pq_begin = pipe->create_query(pipe, type, 0);
161          stq->type = type;
162       }
163       if (stq->pq_begin)
164          ret = pipe->end_query(pipe, stq->pq_begin);
165    } else {
166       if (!stq->pq) {
167          stq->pq = pipe->create_query(pipe, type, q->Stream);
168          stq->type = type;
169       }
170       if (stq->pq)
171          ret = pipe->begin_query(pipe, stq->pq);
172    }
173 
174    if (!ret) {
175       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery");
176 
177       free_queries(pipe, stq);
178       q->Active = GL_FALSE;
179       return;
180    }
181 
182    assert(stq->type == type);
183 }
184 
185 
186 static void
st_EndQuery(struct gl_context * ctx,struct gl_query_object * q)187 st_EndQuery(struct gl_context *ctx, struct gl_query_object *q)
188 {
189    struct pipe_context *pipe = st_context(ctx)->pipe;
190    struct st_query_object *stq = st_query_object(q);
191    bool ret = false;
192 
193    st_flush_bitmap_cache(st_context(ctx));
194 
195    if ((q->Target == GL_TIMESTAMP ||
196         q->Target == GL_TIME_ELAPSED) &&
197        !stq->pq) {
198       stq->pq = pipe->create_query(pipe, PIPE_QUERY_TIMESTAMP, 0);
199       stq->type = PIPE_QUERY_TIMESTAMP;
200    }
201 
202    if (stq->pq)
203       ret = pipe->end_query(pipe, stq->pq);
204 
205    if (!ret) {
206       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glEndQuery");
207       return;
208    }
209 }
210 
211 
212 static boolean
get_query_result(struct pipe_context * pipe,struct st_query_object * stq,boolean wait)213 get_query_result(struct pipe_context *pipe,
214                  struct st_query_object *stq,
215                  boolean wait)
216 {
217    union pipe_query_result data;
218 
219    if (!stq->pq) {
220       /* Only needed in case we failed to allocate the gallium query earlier.
221        * Return TRUE so we don't spin on this forever.
222        */
223       return TRUE;
224    }
225 
226    if (!pipe->get_query_result(pipe, stq->pq, wait, &data))
227       return FALSE;
228 
229    switch (stq->base.Target) {
230    case GL_VERTICES_SUBMITTED_ARB:
231       stq->base.Result = data.pipeline_statistics.ia_vertices;
232       break;
233    case GL_PRIMITIVES_SUBMITTED_ARB:
234       stq->base.Result = data.pipeline_statistics.ia_primitives;
235       break;
236    case GL_VERTEX_SHADER_INVOCATIONS_ARB:
237       stq->base.Result = data.pipeline_statistics.vs_invocations;
238       break;
239    case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
240       stq->base.Result = data.pipeline_statistics.hs_invocations;
241       break;
242    case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
243       stq->base.Result = data.pipeline_statistics.ds_invocations;
244       break;
245    case GL_GEOMETRY_SHADER_INVOCATIONS:
246       stq->base.Result = data.pipeline_statistics.gs_invocations;
247       break;
248    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
249       stq->base.Result = data.pipeline_statistics.gs_primitives;
250       break;
251    case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
252       stq->base.Result = data.pipeline_statistics.ps_invocations;
253       break;
254    case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
255       stq->base.Result = data.pipeline_statistics.cs_invocations;
256       break;
257    case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
258       stq->base.Result = data.pipeline_statistics.c_invocations;
259       break;
260    case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
261       stq->base.Result = data.pipeline_statistics.c_primitives;
262       break;
263    default:
264       switch (stq->type) {
265       case PIPE_QUERY_OCCLUSION_PREDICATE:
266       case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
267       case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
268       case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
269          stq->base.Result = !!data.b;
270          break;
271       default:
272          stq->base.Result = data.u64;
273          break;
274       }
275       break;
276    }
277 
278    if (stq->base.Target == GL_TIME_ELAPSED &&
279        stq->type == PIPE_QUERY_TIMESTAMP) {
280       /* Calculate the elapsed time from the two timestamp queries */
281       GLuint64EXT Result0 = 0;
282       assert(stq->pq_begin);
283       pipe->get_query_result(pipe, stq->pq_begin, TRUE, (void *)&Result0);
284       stq->base.Result -= Result0;
285    } else {
286       assert(!stq->pq_begin);
287    }
288 
289    return TRUE;
290 }
291 
292 
293 static void
st_WaitQuery(struct gl_context * ctx,struct gl_query_object * q)294 st_WaitQuery(struct gl_context *ctx, struct gl_query_object *q)
295 {
296    struct pipe_context *pipe = st_context(ctx)->pipe;
297    struct st_query_object *stq = st_query_object(q);
298 
299    /* this function should only be called if we don't have a ready result */
300    assert(!stq->base.Ready);
301 
302    while (!stq->base.Ready &&
303 	  !get_query_result(pipe, stq, TRUE))
304    {
305       /* nothing */
306    }
307 
308    q->Ready = GL_TRUE;
309 }
310 
311 
312 static void
st_CheckQuery(struct gl_context * ctx,struct gl_query_object * q)313 st_CheckQuery(struct gl_context *ctx, struct gl_query_object *q)
314 {
315    struct pipe_context *pipe = st_context(ctx)->pipe;
316    struct st_query_object *stq = st_query_object(q);
317    assert(!q->Ready);   /* we should not get called if Ready is TRUE */
318    q->Ready = get_query_result(pipe, stq, FALSE);
319 }
320 
321 
322 static uint64_t
st_GetTimestamp(struct gl_context * ctx)323 st_GetTimestamp(struct gl_context *ctx)
324 {
325    struct pipe_context *pipe = st_context(ctx)->pipe;
326    struct pipe_screen *screen = pipe->screen;
327 
328    /* Prefer the per-screen function */
329    if (screen->get_timestamp) {
330       return screen->get_timestamp(screen);
331    }
332    else {
333       /* Fall back to the per-context function */
334       assert(pipe->get_timestamp);
335       return pipe->get_timestamp(pipe);
336    }
337 }
338 
339 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)340 st_StoreQueryResult(struct gl_context *ctx, struct gl_query_object *q,
341                     struct gl_buffer_object *buf, intptr_t offset,
342                     GLenum pname, GLenum ptype)
343 {
344    struct pipe_context *pipe = st_context(ctx)->pipe;
345    struct st_query_object *stq = st_query_object(q);
346    struct st_buffer_object *stObj = st_buffer_object(buf);
347    boolean wait = pname == GL_QUERY_RESULT;
348    enum pipe_query_value_type result_type;
349    int index;
350 
351    /* GL_QUERY_TARGET is a bit of an extension since it has nothing to
352     * do with the GPU end of the query. Write it in "by hand".
353     */
354    if (pname == GL_QUERY_TARGET) {
355       /* Assume that the data must be LE. The endianness situation wrt CPU and
356        * GPU is incredibly confusing, but the vast majority of GPUs are
357        * LE. When a BE one comes along, this needs some form of resolution.
358        */
359       unsigned data[2] = { CPU_TO_LE32(q->Target), 0 };
360       pipe_buffer_write(pipe, stObj->buffer, offset,
361                         (ptype == GL_INT64_ARB ||
362                          ptype == GL_UNSIGNED_INT64_ARB) ? 8 : 4,
363                         data);
364       return;
365    }
366 
367    switch (ptype) {
368    case GL_INT:
369       result_type = PIPE_QUERY_TYPE_I32;
370       break;
371    case GL_UNSIGNED_INT:
372       result_type = PIPE_QUERY_TYPE_U32;
373       break;
374    case GL_INT64_ARB:
375       result_type = PIPE_QUERY_TYPE_I64;
376       break;
377    case GL_UNSIGNED_INT64_ARB:
378       result_type = PIPE_QUERY_TYPE_U64;
379       break;
380    default:
381       unreachable("Unexpected result type");
382    }
383 
384    if (pname == GL_QUERY_RESULT_AVAILABLE) {
385       index = -1;
386    } else if (stq->type == PIPE_QUERY_PIPELINE_STATISTICS) {
387       switch (q->Target) {
388       case GL_VERTICES_SUBMITTED_ARB:
389          index = 0;
390          break;
391       case GL_PRIMITIVES_SUBMITTED_ARB:
392          index = 1;
393          break;
394       case GL_VERTEX_SHADER_INVOCATIONS_ARB:
395          index = 2;
396          break;
397       case GL_GEOMETRY_SHADER_INVOCATIONS:
398          index = 3;
399          break;
400       case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
401          index = 4;
402          break;
403       case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
404          index = 5;
405          break;
406       case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
407          index = 6;
408          break;
409       case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
410          index = 7;
411          break;
412       case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
413          index = 8;
414          break;
415       case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
416          index = 9;
417          break;
418       case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
419          index = 10;
420          break;
421       default:
422          unreachable("Unexpected target");
423       }
424    } else {
425       index = 0;
426    }
427 
428    pipe->get_query_result_resource(pipe, stq->pq, wait, result_type, index,
429                                    stObj->buffer, offset);
430 }
431 
st_init_query_functions(struct dd_function_table * functions)432 void st_init_query_functions(struct dd_function_table *functions)
433 {
434    functions->NewQueryObject = st_NewQueryObject;
435    functions->DeleteQuery = st_DeleteQuery;
436    functions->BeginQuery = st_BeginQuery;
437    functions->EndQuery = st_EndQuery;
438    functions->WaitQuery = st_WaitQuery;
439    functions->CheckQuery = st_CheckQuery;
440    functions->GetTimestamp = st_GetTimestamp;
441    functions->StoreQueryResult = st_StoreQueryResult;
442 }
443