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