• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Mesa 3-D graphics library
3  *
4  * Copyright (C) 1999-2007  Brian Paul   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 "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 
26 #include "bufferobj.h"
27 #include "glheader.h"
28 #include "context.h"
29 #include "enums.h"
30 #include "hash.h"
31 #include "imports.h"
32 #include "queryobj.h"
33 #include "mtypes.h"
34 #include "main/dispatch.h"
35 
36 
37 /**
38  * Allocate a new query object.  This is a fallback routine called via
39  * ctx->Driver.NewQueryObject().
40  * \param ctx - rendering context
41  * \param id - the new object's ID
42  * \return pointer to new query_object object or NULL if out of memory.
43  */
44 static struct gl_query_object *
_mesa_new_query_object(struct gl_context * ctx,GLuint id)45 _mesa_new_query_object(struct gl_context *ctx, GLuint id)
46 {
47    struct gl_query_object *q = CALLOC_STRUCT(gl_query_object);
48    (void) ctx;
49    if (q) {
50       q->Id = id;
51       q->Result = 0;
52       q->Active = GL_FALSE;
53 
54       /* This is to satisfy the language of the specification: "In the initial
55        * state of a query object, the result is available" (OpenGL 3.1 §
56        * 2.13).
57        */
58       q->Ready = GL_TRUE;
59 
60       /* OpenGL 3.1 § 2.13 says about GenQueries, "These names are marked as
61        * used, but no object is associated with them until the first time they
62        * are used by BeginQuery." Since our implementation actually does
63        * allocate an object at this point, use a flag to indicate that this
64        * object has not yet been bound so should not be considered a query.
65        */
66       q->EverBound = GL_FALSE;
67    }
68    return q;
69 }
70 
71 
72 /**
73  * Begin a query.  Software driver fallback.
74  * Called via ctx->Driver.BeginQuery().
75  */
76 static void
_mesa_begin_query(struct gl_context * ctx,struct gl_query_object * q)77 _mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
78 {
79    ctx->NewState |= _NEW_DEPTH; /* for swrast */
80 }
81 
82 
83 /**
84  * End a query.  Software driver fallback.
85  * Called via ctx->Driver.EndQuery().
86  */
87 static void
_mesa_end_query(struct gl_context * ctx,struct gl_query_object * q)88 _mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
89 {
90    ctx->NewState |= _NEW_DEPTH; /* for swrast */
91    q->Ready = GL_TRUE;
92 }
93 
94 
95 /**
96  * Wait for query to complete.  Software driver fallback.
97  * Called via ctx->Driver.WaitQuery().
98  */
99 static void
_mesa_wait_query(struct gl_context * ctx,struct gl_query_object * q)100 _mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
101 {
102    /* For software drivers, _mesa_end_query() should have completed the query.
103     * For real hardware, implement a proper WaitQuery() driver function,
104     * which may require issuing a flush.
105     */
106    assert(q->Ready);
107 }
108 
109 
110 /**
111  * Check if a query results are ready.  Software driver fallback.
112  * Called via ctx->Driver.CheckQuery().
113  */
114 static void
_mesa_check_query(struct gl_context * ctx,struct gl_query_object * q)115 _mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
116 {
117    /* No-op for sw rendering.
118     * HW drivers may need to flush at this time.
119     */
120 }
121 
122 
123 /**
124  * Delete a query object.  Called via ctx->Driver.DeleteQuery().
125  * Not removed from hash table here.
126  */
127 static void
_mesa_delete_query(struct gl_context * ctx,struct gl_query_object * q)128 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
129 {
130    free(q->Label);
131    free(q);
132 }
133 
134 
135 void
_mesa_init_query_object_functions(struct dd_function_table * driver)136 _mesa_init_query_object_functions(struct dd_function_table *driver)
137 {
138    driver->NewQueryObject = _mesa_new_query_object;
139    driver->DeleteQuery = _mesa_delete_query;
140    driver->BeginQuery = _mesa_begin_query;
141    driver->EndQuery = _mesa_end_query;
142    driver->WaitQuery = _mesa_wait_query;
143    driver->CheckQuery = _mesa_check_query;
144 }
145 
146 static struct gl_query_object **
get_pipe_stats_binding_point(struct gl_context * ctx,GLenum target)147 get_pipe_stats_binding_point(struct gl_context *ctx,
148                              GLenum target)
149 {
150    const int which = target - GL_VERTICES_SUBMITTED_ARB;
151    assert(which < MAX_PIPELINE_STATISTICS);
152 
153    if (!_mesa_is_desktop_gl(ctx) ||
154        !ctx->Extensions.ARB_pipeline_statistics_query)
155       return NULL;
156 
157    return &ctx->Query.pipeline_stats[which];
158 }
159 
160 /**
161  * Return pointer to the query object binding point for the given target and
162  * index.
163  * \return NULL if invalid target, else the address of binding point
164  */
165 static struct gl_query_object **
get_query_binding_point(struct gl_context * ctx,GLenum target,GLuint index)166 get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
167 {
168    switch (target) {
169    case GL_SAMPLES_PASSED_ARB:
170       if (ctx->Extensions.ARB_occlusion_query)
171          return &ctx->Query.CurrentOcclusionObject;
172       else
173          return NULL;
174    case GL_ANY_SAMPLES_PASSED:
175       if (ctx->Extensions.ARB_occlusion_query2)
176          return &ctx->Query.CurrentOcclusionObject;
177       else
178          return NULL;
179    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
180       if (ctx->Extensions.ARB_ES3_compatibility
181           || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
182          return &ctx->Query.CurrentOcclusionObject;
183       else
184          return NULL;
185    case GL_TIME_ELAPSED_EXT:
186       if (ctx->Extensions.EXT_timer_query)
187          return &ctx->Query.CurrentTimerObject;
188       else
189          return NULL;
190    case GL_PRIMITIVES_GENERATED:
191       if (ctx->Extensions.EXT_transform_feedback)
192          return &ctx->Query.PrimitivesGenerated[index];
193       else
194          return NULL;
195    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
196       if (ctx->Extensions.EXT_transform_feedback)
197          return &ctx->Query.PrimitivesWritten[index];
198       else
199          return NULL;
200 
201    case GL_VERTICES_SUBMITTED_ARB:
202    case GL_PRIMITIVES_SUBMITTED_ARB:
203    case GL_VERTEX_SHADER_INVOCATIONS_ARB:
204    case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
205    case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
206    case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
207          return get_pipe_stats_binding_point(ctx, target);
208 
209    case GL_GEOMETRY_SHADER_INVOCATIONS:
210       /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */
211       target = GL_VERTICES_SUBMITTED_ARB + MAX_PIPELINE_STATISTICS - 1;
212       /* fallthrough */
213    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
214       if (_mesa_has_geometry_shaders(ctx))
215          return get_pipe_stats_binding_point(ctx, target);
216       else
217          return NULL;
218 
219    case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
220    case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
221       if (_mesa_has_tessellation(ctx))
222          return get_pipe_stats_binding_point(ctx, target);
223       else
224          return NULL;
225 
226    case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
227       if (_mesa_has_compute_shaders(ctx))
228          return get_pipe_stats_binding_point(ctx, target);
229       else
230          return NULL;
231 
232    default:
233       return NULL;
234    }
235 }
236 
237 /**
238  * Create $n query objects and store them in *ids. Make them of type $target
239  * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries().
240  */
241 static void
create_queries(struct gl_context * ctx,GLenum target,GLsizei n,GLuint * ids,bool dsa)242 create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids,
243                bool dsa)
244 {
245    const char *func = dsa ? "glGenQueries" : "glCreateQueries";
246    GLuint first;
247 
248    if (MESA_VERBOSE & VERBOSE_API)
249       _mesa_debug(ctx, "%s(%d)\n", func, n);
250 
251    if (n < 0) {
252       _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
253       return;
254    }
255 
256    first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
257    if (first) {
258       GLsizei i;
259       for (i = 0; i < n; i++) {
260          struct gl_query_object *q
261             = ctx->Driver.NewQueryObject(ctx, first + i);
262          if (!q) {
263             _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
264             return;
265          } else if (dsa) {
266             /* Do the equivalent of binding the buffer with a target */
267             q->Target = target;
268             q->EverBound = GL_TRUE;
269          }
270          ids[i] = first + i;
271          _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
272       }
273    }
274 }
275 
276 void GLAPIENTRY
_mesa_GenQueries(GLsizei n,GLuint * ids)277 _mesa_GenQueries(GLsizei n, GLuint *ids)
278 {
279    GET_CURRENT_CONTEXT(ctx);
280    create_queries(ctx, 0, n, ids, false);
281 }
282 
283 void GLAPIENTRY
_mesa_CreateQueries(GLenum target,GLsizei n,GLuint * ids)284 _mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids)
285 {
286    GET_CURRENT_CONTEXT(ctx);
287 
288    switch (target) {
289    case GL_SAMPLES_PASSED:
290    case GL_ANY_SAMPLES_PASSED:
291    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
292    case GL_TIME_ELAPSED:
293    case GL_TIMESTAMP:
294    case GL_PRIMITIVES_GENERATED:
295    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
296       break;
297    default:
298       _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)",
299                   _mesa_enum_to_string(target));
300       return;
301    }
302 
303    create_queries(ctx, target, n, ids, true);
304 }
305 
306 
307 void GLAPIENTRY
_mesa_DeleteQueries(GLsizei n,const GLuint * ids)308 _mesa_DeleteQueries(GLsizei n, const GLuint *ids)
309 {
310    GLint i;
311    GET_CURRENT_CONTEXT(ctx);
312    FLUSH_VERTICES(ctx, 0);
313 
314    if (MESA_VERBOSE & VERBOSE_API)
315       _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
316 
317    if (n < 0) {
318       _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
319       return;
320    }
321 
322    for (i = 0; i < n; i++) {
323       if (ids[i] > 0) {
324          struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
325          if (q) {
326             if (q->Active) {
327                struct gl_query_object **bindpt;
328                bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
329                assert(bindpt); /* Should be non-null for active q. */
330                if (bindpt) {
331                   *bindpt = NULL;
332                }
333                q->Active = GL_FALSE;
334                ctx->Driver.EndQuery(ctx, q);
335             }
336             _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
337             ctx->Driver.DeleteQuery(ctx, q);
338          }
339       }
340    }
341 }
342 
343 
344 GLboolean GLAPIENTRY
_mesa_IsQuery(GLuint id)345 _mesa_IsQuery(GLuint id)
346 {
347    struct gl_query_object *q;
348 
349    GET_CURRENT_CONTEXT(ctx);
350    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
351 
352    if (MESA_VERBOSE & VERBOSE_API)
353       _mesa_debug(ctx, "glIsQuery(%u)\n", id);
354 
355    if (id == 0)
356       return GL_FALSE;
357 
358    q = _mesa_lookup_query_object(ctx, id);
359    if (q == NULL)
360       return GL_FALSE;
361 
362    return q->EverBound;
363 }
364 
365 static GLboolean
query_error_check_index(struct gl_context * ctx,GLenum target,GLuint index)366 query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
367 {
368    switch (target) {
369    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
370    case GL_PRIMITIVES_GENERATED:
371       if (index >= ctx->Const.MaxVertexStreams) {
372          _mesa_error(ctx, GL_INVALID_VALUE,
373                      "glBeginQueryIndexed(index>=MaxVertexStreams)");
374          return GL_FALSE;
375       }
376       break;
377    default:
378       if (index > 0) {
379          _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
380          return GL_FALSE;
381       }
382    }
383    return GL_TRUE;
384 }
385 
386 void GLAPIENTRY
_mesa_BeginQueryIndexed(GLenum target,GLuint index,GLuint id)387 _mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
388 {
389    struct gl_query_object *q, **bindpt;
390    GET_CURRENT_CONTEXT(ctx);
391 
392    if (MESA_VERBOSE & VERBOSE_API)
393       _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
394                   _mesa_enum_to_string(target), index, id);
395 
396    if (!query_error_check_index(ctx, target, index))
397       return;
398 
399    FLUSH_VERTICES(ctx, 0);
400 
401    bindpt = get_query_binding_point(ctx, target, index);
402    if (!bindpt) {
403       _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
404       return;
405    }
406 
407    /* From the GL_ARB_occlusion_query spec:
408     *
409     *     "If BeginQueryARB is called while another query is already in
410     *      progress with the same target, an INVALID_OPERATION error is
411     *      generated."
412     */
413    if (*bindpt) {
414       _mesa_error(ctx, GL_INVALID_OPERATION,
415                   "glBeginQuery{Indexed}(target=%s is active)",
416                   _mesa_enum_to_string(target));
417       return;
418    }
419 
420    if (id == 0) {
421       _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
422       return;
423    }
424 
425    q = _mesa_lookup_query_object(ctx, id);
426    if (!q) {
427       if (ctx->API != API_OPENGL_COMPAT) {
428          _mesa_error(ctx, GL_INVALID_OPERATION,
429                      "glBeginQuery{Indexed}(non-gen name)");
430          return;
431       } else {
432          /* create new object */
433          q = ctx->Driver.NewQueryObject(ctx, id);
434          if (!q) {
435             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
436             return;
437          }
438          _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
439       }
440    }
441    else {
442       /* pre-existing object */
443       if (q->Active) {
444          _mesa_error(ctx, GL_INVALID_OPERATION,
445                      "glBeginQuery{Indexed}(query already active)");
446          return;
447       }
448 
449       /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4
450        * spec states:
451        *
452        *     "BeginQuery generates an INVALID_OPERATION error if any of the
453        *      following conditions hold: [...] id is the name of an
454        *      existing query object whose type does not match target; [...]
455        *
456        * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY
457        * OBJECTS AND ASYNCHRONOUS QUERIES, page 43.
458        */
459       if (q->EverBound && q->Target != target) {
460          _mesa_error(ctx, GL_INVALID_OPERATION,
461                      "glBeginQuery{Indexed}(target mismatch)");
462          return;
463       }
464    }
465 
466    /* This possibly changes the target of a buffer allocated by
467     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
468     * the following:
469     *
470     * "CreateQueries adds a <target>, so strictly speaking the <target>
471     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
472     * isn't a selector, so we decided not to change it."
473     *
474     * Updating the target of the query object should be acceptable, so let's
475     * do that.
476     */
477 
478    q->Target = target;
479    q->Active = GL_TRUE;
480    q->Result = 0;
481    q->Ready = GL_FALSE;
482    q->EverBound = GL_TRUE;
483    q->Stream = index;
484 
485    /* XXX should probably refcount query objects */
486    *bindpt = q;
487 
488    ctx->Driver.BeginQuery(ctx, q);
489 }
490 
491 
492 void GLAPIENTRY
_mesa_EndQueryIndexed(GLenum target,GLuint index)493 _mesa_EndQueryIndexed(GLenum target, GLuint index)
494 {
495    struct gl_query_object *q, **bindpt;
496    GET_CURRENT_CONTEXT(ctx);
497 
498    if (MESA_VERBOSE & VERBOSE_API)
499       _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
500                   _mesa_enum_to_string(target), index);
501 
502    if (!query_error_check_index(ctx, target, index))
503       return;
504 
505    FLUSH_VERTICES(ctx, 0);
506 
507    bindpt = get_query_binding_point(ctx, target, index);
508    if (!bindpt) {
509       _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
510       return;
511    }
512 
513    /* XXX should probably refcount query objects */
514    q = *bindpt;
515 
516    /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
517    if (q && q->Target != target) {
518       _mesa_error(ctx, GL_INVALID_OPERATION,
519                   "glEndQuery(target=%s with active query of target %s)",
520                   _mesa_enum_to_string(target),
521                   _mesa_enum_to_string(q->Target));
522       return;
523    }
524 
525    *bindpt = NULL;
526 
527    if (!q || !q->Active) {
528       _mesa_error(ctx, GL_INVALID_OPERATION,
529                   "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
530       return;
531    }
532 
533    q->Active = GL_FALSE;
534    ctx->Driver.EndQuery(ctx, q);
535 }
536 
537 void GLAPIENTRY
_mesa_BeginQuery(GLenum target,GLuint id)538 _mesa_BeginQuery(GLenum target, GLuint id)
539 {
540    _mesa_BeginQueryIndexed(target, 0, id);
541 }
542 
543 void GLAPIENTRY
_mesa_EndQuery(GLenum target)544 _mesa_EndQuery(GLenum target)
545 {
546    _mesa_EndQueryIndexed(target, 0);
547 }
548 
549 void GLAPIENTRY
_mesa_QueryCounter(GLuint id,GLenum target)550 _mesa_QueryCounter(GLuint id, GLenum target)
551 {
552    struct gl_query_object *q;
553    GET_CURRENT_CONTEXT(ctx);
554 
555    if (MESA_VERBOSE & VERBOSE_API)
556       _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
557                   _mesa_enum_to_string(target));
558 
559    /* error checking */
560    if (target != GL_TIMESTAMP) {
561       _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
562       return;
563    }
564 
565    if (id == 0) {
566       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
567       return;
568    }
569 
570    q = _mesa_lookup_query_object(ctx, id);
571    if (!q) {
572       /* XXX the Core profile should throw INVALID_OPERATION here */
573 
574       /* create new object */
575       q = ctx->Driver.NewQueryObject(ctx, id);
576       if (!q) {
577          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
578          return;
579       }
580       _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
581    }
582    else {
583       if (q->Target && q->Target != GL_TIMESTAMP) {
584          _mesa_error(ctx, GL_INVALID_OPERATION,
585                      "glQueryCounter(id has an invalid target)");
586          return;
587       }
588    }
589 
590    if (q->Active) {
591       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
592       return;
593    }
594 
595    /* This possibly changes the target of a buffer allocated by
596     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
597     * the following:
598     *
599     * "CreateQueries adds a <target>, so strictly speaking the <target>
600     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
601     * isn't a selector, so we decided not to change it."
602     *
603     * Updating the target of the query object should be acceptable, so let's
604     * do that.
605     */
606 
607    q->Target = target;
608    q->Result = 0;
609    q->Ready = GL_FALSE;
610    q->EverBound = GL_TRUE;
611 
612    if (ctx->Driver.QueryCounter) {
613       ctx->Driver.QueryCounter(ctx, q);
614    } else {
615       /* QueryCounter is implemented using EndQuery without BeginQuery
616        * in drivers. This is actually Direct3D and Gallium convention.
617        */
618       ctx->Driver.EndQuery(ctx, q);
619    }
620 }
621 
622 
623 void GLAPIENTRY
_mesa_GetQueryIndexediv(GLenum target,GLuint index,GLenum pname,GLint * params)624 _mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
625                         GLint *params)
626 {
627    struct gl_query_object *q = NULL, **bindpt = NULL;
628    GET_CURRENT_CONTEXT(ctx);
629 
630    if (MESA_VERBOSE & VERBOSE_API)
631       _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
632                   _mesa_enum_to_string(target),
633                   index,
634                   _mesa_enum_to_string(pname));
635 
636    if (!query_error_check_index(ctx, target, index))
637       return;
638 
639    if (target == GL_TIMESTAMP) {
640       if (!ctx->Extensions.ARB_timer_query) {
641          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
642          return;
643       }
644    }
645    else {
646       bindpt = get_query_binding_point(ctx, target, index);
647       if (!bindpt) {
648          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
649          return;
650       }
651 
652       q = *bindpt;
653    }
654 
655    switch (pname) {
656       case GL_QUERY_COUNTER_BITS_ARB:
657          switch (target) {
658          case GL_SAMPLES_PASSED:
659             *params = ctx->Const.QueryCounterBits.SamplesPassed;
660             break;
661          case GL_ANY_SAMPLES_PASSED:
662             /* The minimum value of this is 1 if it's nonzero, and the value
663              * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
664              * bits.
665              */
666             *params = 1;
667             break;
668          case GL_TIME_ELAPSED:
669             *params = ctx->Const.QueryCounterBits.TimeElapsed;
670             break;
671          case GL_TIMESTAMP:
672             *params = ctx->Const.QueryCounterBits.Timestamp;
673             break;
674          case GL_PRIMITIVES_GENERATED:
675             *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
676             break;
677          case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
678             *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
679             break;
680          case GL_VERTICES_SUBMITTED_ARB:
681             *params = ctx->Const.QueryCounterBits.VerticesSubmitted;
682             break;
683          case GL_PRIMITIVES_SUBMITTED_ARB:
684             *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted;
685             break;
686          case GL_VERTEX_SHADER_INVOCATIONS_ARB:
687             *params = ctx->Const.QueryCounterBits.VsInvocations;
688             break;
689          case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
690             *params = ctx->Const.QueryCounterBits.TessPatches;
691             break;
692          case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
693             *params = ctx->Const.QueryCounterBits.TessInvocations;
694             break;
695          case GL_GEOMETRY_SHADER_INVOCATIONS:
696             *params = ctx->Const.QueryCounterBits.GsInvocations;
697             break;
698          case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
699             *params = ctx->Const.QueryCounterBits.GsPrimitives;
700             break;
701          case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
702             *params = ctx->Const.QueryCounterBits.FsInvocations;
703             break;
704          case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
705             *params = ctx->Const.QueryCounterBits.ComputeInvocations;
706             break;
707          case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
708             *params = ctx->Const.QueryCounterBits.ClInPrimitives;
709             break;
710          case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
711             *params = ctx->Const.QueryCounterBits.ClOutPrimitives;
712             break;
713          default:
714             _mesa_problem(ctx,
715                           "Unknown target in glGetQueryIndexediv(target = %s)",
716                           _mesa_enum_to_string(target));
717             *params = 0;
718             break;
719          }
720          break;
721       case GL_CURRENT_QUERY_ARB:
722          *params = (q && q->Target == target) ? q->Id : 0;
723          break;
724       default:
725          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
726          return;
727    }
728 }
729 
730 void GLAPIENTRY
_mesa_GetQueryiv(GLenum target,GLenum pname,GLint * params)731 _mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
732 {
733    _mesa_GetQueryIndexediv(target, 0, pname, params);
734 }
735 
736 static void
get_query_object(struct gl_context * ctx,const char * func,GLuint id,GLenum pname,GLenum ptype,struct gl_buffer_object * buf,intptr_t offset)737 get_query_object(struct gl_context *ctx, const char *func,
738                  GLuint id, GLenum pname, GLenum ptype,
739                  struct gl_buffer_object *buf, intptr_t offset)
740 {
741    struct gl_query_object *q = NULL;
742    uint64_t value;
743 
744    if (MESA_VERBOSE & VERBOSE_API)
745       _mesa_debug(ctx, "%s(%u, %s)\n", func, id,
746                   _mesa_enum_to_string(pname));
747 
748    if (id)
749       q = _mesa_lookup_query_object(ctx, id);
750 
751    if (!q || q->Active || !q->EverBound) {
752       _mesa_error(ctx, GL_INVALID_OPERATION,
753                   "%s(id=%d is invalid or active)", func, id);
754       return;
755    }
756 
757    if (buf && buf != ctx->Shared->NullBufferObj) {
758       bool is_64bit = ptype == GL_INT64_ARB ||
759          ptype == GL_UNSIGNED_INT64_ARB;
760       if (!ctx->Extensions.ARB_query_buffer_object) {
761          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func);
762          return;
763       }
764       if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) {
765          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func);
766          return;
767       }
768 
769       if (offset < 0) {
770          _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func);
771          return;
772       }
773 
774       switch (pname) {
775       case GL_QUERY_RESULT:
776       case GL_QUERY_RESULT_NO_WAIT:
777       case GL_QUERY_RESULT_AVAILABLE:
778       case GL_QUERY_TARGET:
779          ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype);
780          return;
781       }
782 
783       /* fall through to get error below */
784    }
785 
786    switch (pname) {
787    case GL_QUERY_RESULT:
788       if (!q->Ready)
789          ctx->Driver.WaitQuery(ctx, q);
790       value = q->Result;
791       break;
792    case GL_QUERY_RESULT_NO_WAIT:
793       if (!ctx->Extensions.ARB_query_buffer_object)
794          goto invalid_enum;
795       ctx->Driver.CheckQuery(ctx, q);
796       if (!q->Ready)
797          return;
798       value = q->Result;
799       break;
800    case GL_QUERY_RESULT_AVAILABLE:
801       if (!q->Ready)
802          ctx->Driver.CheckQuery(ctx, q);
803       value = q->Ready;
804       break;
805    case GL_QUERY_TARGET:
806       value = q->Target;
807       break;
808    default:
809 invalid_enum:
810       _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)",
811                   func, _mesa_enum_to_string(pname));
812       return;
813    }
814 
815    switch (ptype) {
816    case GL_INT: {
817       GLint *param = (GLint *)offset;
818       if (value > 0x7fffffff)
819          *param = 0x7fffffff;
820       else
821          *param = value;
822       break;
823    }
824    case GL_UNSIGNED_INT: {
825       GLuint *param = (GLuint *)offset;
826       if (value > 0xffffffff)
827          *param = 0xffffffff;
828       else
829          *param = value;
830       break;
831    }
832    case GL_INT64_ARB:
833    case GL_UNSIGNED_INT64_ARB: {
834       GLuint64EXT *param = (GLuint64EXT *)offset;
835       *param = value;
836       break;
837    }
838    default:
839       unreachable("unexpected ptype");
840    }
841 }
842 
843 void GLAPIENTRY
_mesa_GetQueryObjectiv(GLuint id,GLenum pname,GLint * params)844 _mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
845 {
846    GET_CURRENT_CONTEXT(ctx);
847 
848    get_query_object(ctx, "glGetQueryObjectiv",
849                     id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params);
850 }
851 
852 
853 void GLAPIENTRY
_mesa_GetQueryObjectuiv(GLuint id,GLenum pname,GLuint * params)854 _mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
855 {
856    GET_CURRENT_CONTEXT(ctx);
857 
858    get_query_object(ctx, "glGetQueryObjectuiv",
859                     id, pname, GL_UNSIGNED_INT,
860                     ctx->QueryBuffer, (intptr_t)params);
861 }
862 
863 
864 /**
865  * New with GL_EXT_timer_query
866  */
867 void GLAPIENTRY
_mesa_GetQueryObjecti64v(GLuint id,GLenum pname,GLint64EXT * params)868 _mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
869 {
870    GET_CURRENT_CONTEXT(ctx);
871 
872    get_query_object(ctx, "glGetQueryObjecti64v",
873                     id, pname, GL_INT64_ARB,
874                     ctx->QueryBuffer, (intptr_t)params);
875 }
876 
877 
878 /**
879  * New with GL_EXT_timer_query
880  */
881 void GLAPIENTRY
_mesa_GetQueryObjectui64v(GLuint id,GLenum pname,GLuint64EXT * params)882 _mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
883 {
884    GET_CURRENT_CONTEXT(ctx);
885 
886    get_query_object(ctx, "glGetQueryObjectui64v",
887                     id, pname, GL_UNSIGNED_INT64_ARB,
888                     ctx->QueryBuffer, (intptr_t)params);
889 }
890 
891 /**
892  * New with GL_ARB_query_buffer_object
893  */
894 void GLAPIENTRY
_mesa_GetQueryBufferObjectiv(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)895 _mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname,
896                              GLintptr offset)
897 {
898    struct gl_buffer_object *buf;
899    GET_CURRENT_CONTEXT(ctx);
900 
901    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv");
902    if (!buf)
903       return;
904 
905    get_query_object(ctx, "glGetQueryBufferObjectiv",
906                     id, pname, GL_INT, buf, offset);
907 }
908 
909 
910 void GLAPIENTRY
_mesa_GetQueryBufferObjectuiv(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)911 _mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname,
912                               GLintptr offset)
913 {
914    struct gl_buffer_object *buf;
915    GET_CURRENT_CONTEXT(ctx);
916 
917    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv");
918    if (!buf)
919       return;
920 
921    get_query_object(ctx, "glGetQueryBufferObjectuiv",
922                     id, pname, GL_UNSIGNED_INT, buf, offset);
923 }
924 
925 
926 void GLAPIENTRY
_mesa_GetQueryBufferObjecti64v(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)927 _mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname,
928                                GLintptr offset)
929 {
930    struct gl_buffer_object *buf;
931    GET_CURRENT_CONTEXT(ctx);
932 
933    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v");
934    if (!buf)
935       return;
936 
937    get_query_object(ctx, "glGetQueryBufferObjecti64v",
938                     id, pname, GL_INT64_ARB, buf, offset);
939 }
940 
941 
942 void GLAPIENTRY
_mesa_GetQueryBufferObjectui64v(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)943 _mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname,
944                                 GLintptr offset)
945 {
946    struct gl_buffer_object *buf;
947    GET_CURRENT_CONTEXT(ctx);
948 
949    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v");
950    if (!buf)
951       return;
952 
953    get_query_object(ctx, "glGetQueryBufferObjectui64v",
954                     id, pname, GL_UNSIGNED_INT64_ARB, buf, offset);
955 }
956 
957 
958 /**
959  * Allocate/init the context state related to query objects.
960  */
961 void
_mesa_init_queryobj(struct gl_context * ctx)962 _mesa_init_queryobj(struct gl_context *ctx)
963 {
964    ctx->Query.QueryObjects = _mesa_NewHashTable();
965    ctx->Query.CurrentOcclusionObject = NULL;
966 
967    ctx->Const.QueryCounterBits.SamplesPassed = 64;
968    ctx->Const.QueryCounterBits.TimeElapsed = 64;
969    ctx->Const.QueryCounterBits.Timestamp = 64;
970    ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
971    ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
972 
973    ctx->Const.QueryCounterBits.VerticesSubmitted = 64;
974    ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64;
975    ctx->Const.QueryCounterBits.VsInvocations = 64;
976    ctx->Const.QueryCounterBits.TessPatches = 64;
977    ctx->Const.QueryCounterBits.TessInvocations = 64;
978    ctx->Const.QueryCounterBits.GsInvocations = 64;
979    ctx->Const.QueryCounterBits.GsPrimitives = 64;
980    ctx->Const.QueryCounterBits.FsInvocations = 64;
981    ctx->Const.QueryCounterBits.ComputeInvocations = 64;
982    ctx->Const.QueryCounterBits.ClInPrimitives = 64;
983    ctx->Const.QueryCounterBits.ClOutPrimitives = 64;
984 }
985 
986 
987 /**
988  * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
989  */
990 static void
delete_queryobj_cb(GLuint id,void * data,void * userData)991 delete_queryobj_cb(GLuint id, void *data, void *userData)
992 {
993    struct gl_query_object *q= (struct gl_query_object *) data;
994    struct gl_context *ctx = (struct gl_context *)userData;
995    ctx->Driver.DeleteQuery(ctx, q);
996 }
997 
998 
999 /**
1000  * Free the context state related to query objects.
1001  */
1002 void
_mesa_free_queryobj_data(struct gl_context * ctx)1003 _mesa_free_queryobj_data(struct gl_context *ctx)
1004 {
1005    _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
1006    _mesa_DeleteHashTable(ctx->Query.QueryObjects);
1007 }
1008