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 
32 #include "queryobj.h"
33 #include "mtypes.h"
34 #include "util/u_memory.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(), if not
125  * overwritten by driver.  In the latter case, called from the driver
126  * after all driver-specific clean-up has been done.
127  * Not removed from hash table here.
128  *
129  * \param ctx GL context to wich query object belongs.
130  * \param q query object due to be deleted.
131  */
132 void
_mesa_delete_query(struct gl_context * ctx,struct gl_query_object * q)133 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
134 {
135    free(q->Label);
136    free(q);
137 }
138 
139 
140 void
_mesa_init_query_object_functions(struct dd_function_table * driver)141 _mesa_init_query_object_functions(struct dd_function_table *driver)
142 {
143    driver->NewQueryObject = _mesa_new_query_object;
144    driver->DeleteQuery = _mesa_delete_query;
145    driver->BeginQuery = _mesa_begin_query;
146    driver->EndQuery = _mesa_end_query;
147    driver->WaitQuery = _mesa_wait_query;
148    driver->CheckQuery = _mesa_check_query;
149 }
150 
151 static struct gl_query_object **
get_pipe_stats_binding_point(struct gl_context * ctx,GLenum target)152 get_pipe_stats_binding_point(struct gl_context *ctx,
153                              GLenum target)
154 {
155    const int which = target - GL_VERTICES_SUBMITTED;
156    assert(which < MAX_PIPELINE_STATISTICS);
157 
158    if (!_mesa_has_ARB_pipeline_statistics_query(ctx))
159       return NULL;
160 
161    return &ctx->Query.pipeline_stats[which];
162 }
163 
164 /**
165  * Return pointer to the query object binding point for the given target and
166  * index.
167  * \return NULL if invalid target, else the address of binding point
168  */
169 static struct gl_query_object **
get_query_binding_point(struct gl_context * ctx,GLenum target,GLuint index)170 get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
171 {
172    switch (target) {
173    case GL_SAMPLES_PASSED:
174       if (_mesa_has_ARB_occlusion_query(ctx) ||
175           _mesa_has_ARB_occlusion_query2(ctx))
176          return &ctx->Query.CurrentOcclusionObject;
177       else
178          return NULL;
179    case GL_ANY_SAMPLES_PASSED:
180       if (_mesa_has_ARB_occlusion_query2(ctx) ||
181           _mesa_has_EXT_occlusion_query_boolean(ctx))
182          return &ctx->Query.CurrentOcclusionObject;
183       else
184          return NULL;
185    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
186       if (_mesa_has_ARB_ES3_compatibility(ctx) ||
187           _mesa_has_EXT_occlusion_query_boolean(ctx))
188          return &ctx->Query.CurrentOcclusionObject;
189       else
190          return NULL;
191    case GL_TIME_ELAPSED:
192       if (_mesa_has_EXT_timer_query(ctx) ||
193           _mesa_has_EXT_disjoint_timer_query(ctx))
194          return &ctx->Query.CurrentTimerObject;
195       else
196          return NULL;
197    case GL_PRIMITIVES_GENERATED:
198       if (_mesa_has_EXT_transform_feedback(ctx) ||
199           _mesa_has_EXT_tessellation_shader(ctx) ||
200           _mesa_has_OES_geometry_shader(ctx))
201          return &ctx->Query.PrimitivesGenerated[index];
202       else
203          return NULL;
204    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
205       if (_mesa_has_EXT_transform_feedback(ctx) || _mesa_is_gles3(ctx))
206          return &ctx->Query.PrimitivesWritten[index];
207       else
208          return NULL;
209    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW:
210       if (_mesa_has_ARB_transform_feedback_overflow_query(ctx))
211          return &ctx->Query.TransformFeedbackOverflow[index];
212       else
213          return NULL;
214    case GL_TRANSFORM_FEEDBACK_OVERFLOW:
215       if (_mesa_has_ARB_transform_feedback_overflow_query(ctx))
216          return &ctx->Query.TransformFeedbackOverflowAny;
217       else
218          return NULL;
219 
220    case GL_VERTICES_SUBMITTED:
221    case GL_PRIMITIVES_SUBMITTED:
222    case GL_VERTEX_SHADER_INVOCATIONS:
223    case GL_FRAGMENT_SHADER_INVOCATIONS:
224    case GL_CLIPPING_INPUT_PRIMITIVES:
225    case GL_CLIPPING_OUTPUT_PRIMITIVES:
226          return get_pipe_stats_binding_point(ctx, target);
227 
228    case GL_GEOMETRY_SHADER_INVOCATIONS:
229       /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */
230       target = GL_VERTICES_SUBMITTED + MAX_PIPELINE_STATISTICS - 1;
231       FALLTHROUGH;
232    case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED:
233       if (_mesa_has_geometry_shaders(ctx))
234          return get_pipe_stats_binding_point(ctx, target);
235       else
236          return NULL;
237 
238    case GL_TESS_CONTROL_SHADER_PATCHES:
239    case GL_TESS_EVALUATION_SHADER_INVOCATIONS:
240       if (_mesa_has_tessellation(ctx))
241          return get_pipe_stats_binding_point(ctx, target);
242       else
243          return NULL;
244 
245    case GL_COMPUTE_SHADER_INVOCATIONS:
246       if (_mesa_has_compute_shaders(ctx))
247          return get_pipe_stats_binding_point(ctx, target);
248       else
249          return NULL;
250 
251    default:
252       return NULL;
253    }
254 }
255 
256 /**
257  * Create $n query objects and store them in *ids. Make them of type $target
258  * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries().
259  */
260 static void
create_queries(struct gl_context * ctx,GLenum target,GLsizei n,GLuint * ids,bool dsa)261 create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids,
262                bool dsa)
263 {
264    const char *func = dsa ? "glGenQueries" : "glCreateQueries";
265 
266    if (MESA_VERBOSE & VERBOSE_API)
267       _mesa_debug(ctx, "%s(%d)\n", func, n);
268 
269    if (n < 0) {
270       _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
271       return;
272    }
273 
274    if (_mesa_HashFindFreeKeys(ctx->Query.QueryObjects, ids, n)) {
275       GLsizei i;
276       for (i = 0; i < n; i++) {
277          struct gl_query_object *q
278             = ctx->Driver.NewQueryObject(ctx, ids[i]);
279          if (!q) {
280             _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
281             return;
282          } else if (dsa) {
283             /* Do the equivalent of binding the buffer with a target */
284             q->Target = target;
285             q->EverBound = GL_TRUE;
286          }
287          _mesa_HashInsertLocked(ctx->Query.QueryObjects, ids[i], q, true);
288       }
289    }
290 }
291 
292 void GLAPIENTRY
_mesa_GenQueries(GLsizei n,GLuint * ids)293 _mesa_GenQueries(GLsizei n, GLuint *ids)
294 {
295    GET_CURRENT_CONTEXT(ctx);
296    create_queries(ctx, 0, n, ids, false);
297 }
298 
299 void GLAPIENTRY
_mesa_CreateQueries(GLenum target,GLsizei n,GLuint * ids)300 _mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids)
301 {
302    GET_CURRENT_CONTEXT(ctx);
303 
304    switch (target) {
305    case GL_SAMPLES_PASSED:
306    case GL_ANY_SAMPLES_PASSED:
307    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
308    case GL_TIME_ELAPSED:
309    case GL_TIMESTAMP:
310    case GL_PRIMITIVES_GENERATED:
311    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
312    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW:
313    case GL_TRANSFORM_FEEDBACK_OVERFLOW:
314       break;
315    default:
316       _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)",
317                   _mesa_enum_to_string(target));
318       return;
319    }
320 
321    create_queries(ctx, target, n, ids, true);
322 }
323 
324 
325 void GLAPIENTRY
_mesa_DeleteQueries(GLsizei n,const GLuint * ids)326 _mesa_DeleteQueries(GLsizei n, const GLuint *ids)
327 {
328    GLint i;
329    GET_CURRENT_CONTEXT(ctx);
330    FLUSH_VERTICES(ctx, 0, 0);
331 
332    if (MESA_VERBOSE & VERBOSE_API)
333       _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
334 
335    if (n < 0) {
336       _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
337       return;
338    }
339 
340    for (i = 0; i < n; i++) {
341       if (ids[i] > 0) {
342          struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
343          if (q) {
344             if (q->Active) {
345                struct gl_query_object **bindpt;
346                bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
347                assert(bindpt); /* Should be non-null for active q. */
348                if (bindpt) {
349                   *bindpt = NULL;
350                }
351                q->Active = GL_FALSE;
352                ctx->Driver.EndQuery(ctx, q);
353             }
354             _mesa_HashRemoveLocked(ctx->Query.QueryObjects, ids[i]);
355             ctx->Driver.DeleteQuery(ctx, q);
356          }
357       }
358    }
359 }
360 
361 
362 GLboolean GLAPIENTRY
_mesa_IsQuery(GLuint id)363 _mesa_IsQuery(GLuint id)
364 {
365    struct gl_query_object *q;
366 
367    GET_CURRENT_CONTEXT(ctx);
368    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
369 
370    if (MESA_VERBOSE & VERBOSE_API)
371       _mesa_debug(ctx, "glIsQuery(%u)\n", id);
372 
373    if (id == 0)
374       return GL_FALSE;
375 
376    q = _mesa_lookup_query_object(ctx, id);
377    if (q == NULL)
378       return GL_FALSE;
379 
380    return q->EverBound;
381 }
382 
383 static GLboolean
query_error_check_index(struct gl_context * ctx,GLenum target,GLuint index)384 query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
385 {
386    switch (target) {
387    case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
388    case GL_PRIMITIVES_GENERATED:
389    case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW:
390       if (index >= ctx->Const.MaxVertexStreams) {
391          _mesa_error(ctx, GL_INVALID_VALUE,
392                      "glBeginQueryIndexed(index>=MaxVertexStreams)");
393          return GL_FALSE;
394       }
395       break;
396    default:
397       if (index > 0) {
398          _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
399          return GL_FALSE;
400       }
401    }
402    return GL_TRUE;
403 }
404 
405 void GLAPIENTRY
_mesa_BeginQueryIndexed(GLenum target,GLuint index,GLuint id)406 _mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
407 {
408    struct gl_query_object *q, **bindpt;
409    GET_CURRENT_CONTEXT(ctx);
410 
411    if (MESA_VERBOSE & VERBOSE_API)
412       _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
413                   _mesa_enum_to_string(target), index, id);
414 
415    if (!query_error_check_index(ctx, target, index))
416       return;
417 
418    FLUSH_VERTICES(ctx, 0, 0);
419 
420    bindpt = get_query_binding_point(ctx, target, index);
421    if (!bindpt) {
422       _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
423       return;
424    }
425 
426    /* From the GL_ARB_occlusion_query spec:
427     *
428     *     "If BeginQueryARB is called while another query is already in
429     *      progress with the same target, an INVALID_OPERATION error is
430     *      generated."
431     */
432    if (*bindpt) {
433       _mesa_error(ctx, GL_INVALID_OPERATION,
434                   "glBeginQuery{Indexed}(target=%s is active)",
435                   _mesa_enum_to_string(target));
436       return;
437    }
438 
439    if (id == 0) {
440       _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
441       return;
442    }
443 
444    q = _mesa_lookup_query_object(ctx, id);
445    if (!q) {
446       if (ctx->API != API_OPENGL_COMPAT) {
447          _mesa_error(ctx, GL_INVALID_OPERATION,
448                      "glBeginQuery{Indexed}(non-gen name)");
449          return;
450       } else {
451          /* create new object */
452          q = ctx->Driver.NewQueryObject(ctx, id);
453          if (!q) {
454             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
455             return;
456          }
457          _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q, false);
458       }
459    }
460    else {
461       /* pre-existing object */
462       if (q->Active) {
463          _mesa_error(ctx, GL_INVALID_OPERATION,
464                      "glBeginQuery{Indexed}(query already active)");
465          return;
466       }
467 
468       /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4
469        * spec states:
470        *
471        *     "BeginQuery generates an INVALID_OPERATION error if any of the
472        *      following conditions hold: [...] id is the name of an
473        *      existing query object whose type does not match target; [...]
474        *
475        * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY
476        * OBJECTS AND ASYNCHRONOUS QUERIES, page 43.
477        */
478       if (q->EverBound && q->Target != target) {
479          _mesa_error(ctx, GL_INVALID_OPERATION,
480                      "glBeginQuery{Indexed}(target mismatch)");
481          return;
482       }
483    }
484 
485    /* This possibly changes the target of a buffer allocated by
486     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
487     * the following:
488     *
489     * "CreateQueries adds a <target>, so strictly speaking the <target>
490     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
491     * isn't a selector, so we decided not to change it."
492     *
493     * Updating the target of the query object should be acceptable, so let's
494     * do that.
495     */
496 
497    q->Target = target;
498    q->Active = GL_TRUE;
499    q->Result = 0;
500    q->Ready = GL_FALSE;
501    q->EverBound = GL_TRUE;
502    q->Stream = index;
503 
504    /* XXX should probably refcount query objects */
505    *bindpt = q;
506 
507    ctx->Driver.BeginQuery(ctx, q);
508 }
509 
510 
511 void GLAPIENTRY
_mesa_EndQueryIndexed(GLenum target,GLuint index)512 _mesa_EndQueryIndexed(GLenum target, GLuint index)
513 {
514    struct gl_query_object *q, **bindpt;
515    GET_CURRENT_CONTEXT(ctx);
516 
517    if (MESA_VERBOSE & VERBOSE_API)
518       _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
519                   _mesa_enum_to_string(target), index);
520 
521    if (!query_error_check_index(ctx, target, index))
522       return;
523 
524    FLUSH_VERTICES(ctx, 0, 0);
525 
526    bindpt = get_query_binding_point(ctx, target, index);
527    if (!bindpt) {
528       _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
529       return;
530    }
531 
532    /* XXX should probably refcount query objects */
533    q = *bindpt;
534 
535    /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
536    if (q && q->Target != target) {
537       _mesa_error(ctx, GL_INVALID_OPERATION,
538                   "glEndQuery(target=%s with active query of target %s)",
539                   _mesa_enum_to_string(target),
540                   _mesa_enum_to_string(q->Target));
541       return;
542    }
543 
544    *bindpt = NULL;
545 
546    if (!q || !q->Active) {
547       _mesa_error(ctx, GL_INVALID_OPERATION,
548                   "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
549       return;
550    }
551 
552    q->Active = GL_FALSE;
553    ctx->Driver.EndQuery(ctx, q);
554 }
555 
556 void GLAPIENTRY
_mesa_BeginQuery(GLenum target,GLuint id)557 _mesa_BeginQuery(GLenum target, GLuint id)
558 {
559    _mesa_BeginQueryIndexed(target, 0, id);
560 }
561 
562 void GLAPIENTRY
_mesa_EndQuery(GLenum target)563 _mesa_EndQuery(GLenum target)
564 {
565    _mesa_EndQueryIndexed(target, 0);
566 }
567 
568 void GLAPIENTRY
_mesa_QueryCounter(GLuint id,GLenum target)569 _mesa_QueryCounter(GLuint id, GLenum target)
570 {
571    struct gl_query_object *q;
572    GET_CURRENT_CONTEXT(ctx);
573 
574    if (MESA_VERBOSE & VERBOSE_API)
575       _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
576                   _mesa_enum_to_string(target));
577 
578    /* error checking */
579    if (target != GL_TIMESTAMP) {
580       _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
581       return;
582    }
583 
584    if (id == 0) {
585       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
586       return;
587    }
588 
589    q = _mesa_lookup_query_object(ctx, id);
590    if (!q) {
591       /* XXX the Core profile should throw INVALID_OPERATION here */
592 
593       /* create new object */
594       q = ctx->Driver.NewQueryObject(ctx, id);
595       if (!q) {
596          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
597          return;
598       }
599       _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q, false);
600    }
601    else {
602       if (q->Target && q->Target != GL_TIMESTAMP) {
603          _mesa_error(ctx, GL_INVALID_OPERATION,
604                      "glQueryCounter(id has an invalid target)");
605          return;
606       }
607    }
608 
609    if (q->Active) {
610       _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
611       return;
612    }
613 
614    /* This possibly changes the target of a buffer allocated by
615     * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
616     * the following:
617     *
618     * "CreateQueries adds a <target>, so strictly speaking the <target>
619     * command isn't needed for BeginQuery/EndQuery, but in the end, this also
620     * isn't a selector, so we decided not to change it."
621     *
622     * Updating the target of the query object should be acceptable, so let's
623     * do that.
624     */
625 
626    q->Target = target;
627    q->Result = 0;
628    q->Ready = GL_FALSE;
629    q->EverBound = GL_TRUE;
630 
631    if (ctx->Driver.QueryCounter) {
632       ctx->Driver.QueryCounter(ctx, q);
633    } else {
634       /* QueryCounter is implemented using EndQuery without BeginQuery
635        * in drivers. This is actually Direct3D and Gallium convention.
636        */
637       ctx->Driver.EndQuery(ctx, q);
638    }
639 }
640 
641 
642 void GLAPIENTRY
_mesa_GetQueryIndexediv(GLenum target,GLuint index,GLenum pname,GLint * params)643 _mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
644                         GLint *params)
645 {
646    struct gl_query_object *q = NULL, **bindpt = NULL;
647    GET_CURRENT_CONTEXT(ctx);
648 
649    if (MESA_VERBOSE & VERBOSE_API)
650       _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
651                   _mesa_enum_to_string(target),
652                   index,
653                   _mesa_enum_to_string(pname));
654 
655    if (!query_error_check_index(ctx, target, index))
656       return;
657 
658    /* From the GL_EXT_occlusion_query_boolean spec:
659     *
660     * "The error INVALID_ENUM is generated if GetQueryivEXT is called where
661     * <pname> is not CURRENT_QUERY_EXT."
662     *
663     * Same rule is present also in ES 3.2 spec.
664     *
665     * EXT_disjoint_timer_query extends this with GL_QUERY_COUNTER_BITS.
666     */
667    if (_mesa_is_gles(ctx)) {
668       switch (pname) {
669       case GL_CURRENT_QUERY:
670          break;
671       case GL_QUERY_COUNTER_BITS:
672          if (_mesa_has_EXT_disjoint_timer_query(ctx))
673             break;
674          FALLTHROUGH;
675       default:
676          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivEXT(%s)",
677                      _mesa_enum_to_string(pname));
678       }
679    }
680 
681    if (target == GL_TIMESTAMP) {
682       if (!_mesa_has_ARB_timer_query(ctx) &&
683           !_mesa_has_EXT_disjoint_timer_query(ctx)) {
684          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
685          return;
686       }
687    }
688    else {
689       bindpt = get_query_binding_point(ctx, target, index);
690       if (!bindpt) {
691          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
692          return;
693       }
694 
695       q = *bindpt;
696    }
697 
698    switch (pname) {
699       case GL_QUERY_COUNTER_BITS:
700          switch (target) {
701          case GL_SAMPLES_PASSED:
702             *params = ctx->Const.QueryCounterBits.SamplesPassed;
703             break;
704          case GL_ANY_SAMPLES_PASSED:
705          case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
706             /* The minimum value of this is 1 if it's nonzero, and the value
707              * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
708              * bits.
709              */
710             *params = 1;
711             break;
712          case GL_TIME_ELAPSED:
713             *params = ctx->Const.QueryCounterBits.TimeElapsed;
714             break;
715          case GL_TIMESTAMP:
716             *params = ctx->Const.QueryCounterBits.Timestamp;
717             break;
718          case GL_PRIMITIVES_GENERATED:
719             *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
720             break;
721          case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
722             *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
723             break;
724          case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW:
725          case GL_TRANSFORM_FEEDBACK_OVERFLOW:
726             /* The minimum value of this is 1 if it's nonzero, and the value
727              * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
728              * bits.
729              */
730             *params = 1;
731             break;
732          case GL_VERTICES_SUBMITTED:
733             *params = ctx->Const.QueryCounterBits.VerticesSubmitted;
734             break;
735          case GL_PRIMITIVES_SUBMITTED:
736             *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted;
737             break;
738          case GL_VERTEX_SHADER_INVOCATIONS:
739             *params = ctx->Const.QueryCounterBits.VsInvocations;
740             break;
741          case GL_TESS_CONTROL_SHADER_PATCHES:
742             *params = ctx->Const.QueryCounterBits.TessPatches;
743             break;
744          case GL_TESS_EVALUATION_SHADER_INVOCATIONS:
745             *params = ctx->Const.QueryCounterBits.TessInvocations;
746             break;
747          case GL_GEOMETRY_SHADER_INVOCATIONS:
748             *params = ctx->Const.QueryCounterBits.GsInvocations;
749             break;
750          case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED:
751             *params = ctx->Const.QueryCounterBits.GsPrimitives;
752             break;
753          case GL_FRAGMENT_SHADER_INVOCATIONS:
754             *params = ctx->Const.QueryCounterBits.FsInvocations;
755             break;
756          case GL_COMPUTE_SHADER_INVOCATIONS:
757             *params = ctx->Const.QueryCounterBits.ComputeInvocations;
758             break;
759          case GL_CLIPPING_INPUT_PRIMITIVES:
760             *params = ctx->Const.QueryCounterBits.ClInPrimitives;
761             break;
762          case GL_CLIPPING_OUTPUT_PRIMITIVES:
763             *params = ctx->Const.QueryCounterBits.ClOutPrimitives;
764             break;
765          default:
766             _mesa_problem(ctx,
767                           "Unknown target in glGetQueryIndexediv(target = %s)",
768                           _mesa_enum_to_string(target));
769             *params = 0;
770             break;
771          }
772          break;
773       case GL_CURRENT_QUERY:
774          *params = (q && q->Target == target) ? q->Id : 0;
775          break;
776       default:
777          _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
778          return;
779    }
780 }
781 
782 void GLAPIENTRY
_mesa_GetQueryiv(GLenum target,GLenum pname,GLint * params)783 _mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
784 {
785    _mesa_GetQueryIndexediv(target, 0, pname, params);
786 }
787 
788 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)789 get_query_object(struct gl_context *ctx, const char *func,
790                  GLuint id, GLenum pname, GLenum ptype,
791                  struct gl_buffer_object *buf, intptr_t offset)
792 {
793    struct gl_query_object *q = NULL;
794    uint64_t value;
795 
796    if (MESA_VERBOSE & VERBOSE_API)
797       _mesa_debug(ctx, "%s(%u, %s)\n", func, id,
798                   _mesa_enum_to_string(pname));
799 
800    if (id)
801       q = _mesa_lookup_query_object(ctx, id);
802 
803    if (!q || q->Active || !q->EverBound) {
804       _mesa_error(ctx, GL_INVALID_OPERATION,
805                   "%s(id=%d is invalid or active)", func, id);
806       return;
807    }
808 
809    /* From GL_EXT_occlusion_query_boolean spec:
810     *
811     *    "Accepted by the <pname> parameter of GetQueryObjectivEXT and
812     *    GetQueryObjectuivEXT:
813     *
814     *    QUERY_RESULT_EXT                               0x8866
815     *    QUERY_RESULT_AVAILABLE_EXT                     0x8867"
816     *
817     * Same rule is present also in ES 3.2 spec.
818     */
819    if (_mesa_is_gles(ctx) &&
820        (pname != GL_QUERY_RESULT && pname != GL_QUERY_RESULT_AVAILABLE)) {
821       _mesa_error(ctx, GL_INVALID_ENUM, "%s(%s)", func,
822                   _mesa_enum_to_string(pname));
823       return;
824    }
825 
826    if (buf) {
827       bool is_64bit = ptype == GL_INT64_ARB ||
828          ptype == GL_UNSIGNED_INT64_ARB;
829       if (!_mesa_has_ARB_query_buffer_object(ctx)) {
830          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func);
831          return;
832       }
833       if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) {
834          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func);
835          return;
836       }
837 
838       if (offset < 0) {
839          _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func);
840          return;
841       }
842 
843       switch (pname) {
844       case GL_QUERY_RESULT:
845       case GL_QUERY_RESULT_NO_WAIT:
846       case GL_QUERY_RESULT_AVAILABLE:
847       case GL_QUERY_TARGET:
848          ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype);
849          return;
850       }
851 
852       /* fall through to get error below */
853    }
854 
855    switch (pname) {
856    case GL_QUERY_RESULT:
857       if (!q->Ready)
858          ctx->Driver.WaitQuery(ctx, q);
859       value = q->Result;
860       break;
861    case GL_QUERY_RESULT_NO_WAIT:
862       if (!_mesa_has_ARB_query_buffer_object(ctx))
863          goto invalid_enum;
864       ctx->Driver.CheckQuery(ctx, q);
865       if (!q->Ready)
866          return;
867       value = q->Result;
868       break;
869    case GL_QUERY_RESULT_AVAILABLE:
870       if (!q->Ready)
871          ctx->Driver.CheckQuery(ctx, q);
872       value = q->Ready;
873       break;
874    case GL_QUERY_TARGET:
875       value = q->Target;
876       break;
877    default:
878 invalid_enum:
879       _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)",
880                   func, _mesa_enum_to_string(pname));
881       return;
882    }
883 
884    switch (ptype) {
885    case GL_INT: {
886       GLint *param = (GLint *)offset;
887       if (value > 0x7fffffff)
888          *param = 0x7fffffff;
889       else
890          *param = value;
891       break;
892    }
893    case GL_UNSIGNED_INT: {
894       GLuint *param = (GLuint *)offset;
895       if (value > 0xffffffff)
896          *param = 0xffffffff;
897       else
898          *param = value;
899       break;
900    }
901    case GL_INT64_ARB:
902    case GL_UNSIGNED_INT64_ARB: {
903       GLuint64EXT *param = (GLuint64EXT *)offset;
904       *param = value;
905       break;
906    }
907    default:
908       unreachable("unexpected ptype");
909    }
910 }
911 
912 void GLAPIENTRY
_mesa_GetQueryObjectiv(GLuint id,GLenum pname,GLint * params)913 _mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
914 {
915    GET_CURRENT_CONTEXT(ctx);
916 
917    get_query_object(ctx, "glGetQueryObjectiv",
918                     id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params);
919 }
920 
921 
922 void GLAPIENTRY
_mesa_GetQueryObjectuiv(GLuint id,GLenum pname,GLuint * params)923 _mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
924 {
925    GET_CURRENT_CONTEXT(ctx);
926 
927    get_query_object(ctx, "glGetQueryObjectuiv",
928                     id, pname, GL_UNSIGNED_INT,
929                     ctx->QueryBuffer, (intptr_t)params);
930 }
931 
932 
933 /**
934  * New with GL_EXT_timer_query
935  */
936 void GLAPIENTRY
_mesa_GetQueryObjecti64v(GLuint id,GLenum pname,GLint64EXT * params)937 _mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
938 {
939    GET_CURRENT_CONTEXT(ctx);
940 
941    get_query_object(ctx, "glGetQueryObjecti64v",
942                     id, pname, GL_INT64_ARB,
943                     ctx->QueryBuffer, (intptr_t)params);
944 }
945 
946 
947 /**
948  * New with GL_EXT_timer_query
949  */
950 void GLAPIENTRY
_mesa_GetQueryObjectui64v(GLuint id,GLenum pname,GLuint64EXT * params)951 _mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
952 {
953    GET_CURRENT_CONTEXT(ctx);
954 
955    get_query_object(ctx, "glGetQueryObjectui64v",
956                     id, pname, GL_UNSIGNED_INT64_ARB,
957                     ctx->QueryBuffer, (intptr_t)params);
958 }
959 
960 /**
961  * New with GL_ARB_query_buffer_object
962  */
963 void GLAPIENTRY
_mesa_GetQueryBufferObjectiv(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)964 _mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname,
965                              GLintptr offset)
966 {
967    struct gl_buffer_object *buf;
968    GET_CURRENT_CONTEXT(ctx);
969 
970    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv");
971    if (!buf)
972       return;
973 
974    get_query_object(ctx, "glGetQueryBufferObjectiv",
975                     id, pname, GL_INT, buf, offset);
976 }
977 
978 
979 void GLAPIENTRY
_mesa_GetQueryBufferObjectuiv(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)980 _mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname,
981                               GLintptr offset)
982 {
983    struct gl_buffer_object *buf;
984    GET_CURRENT_CONTEXT(ctx);
985 
986    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv");
987    if (!buf)
988       return;
989 
990    get_query_object(ctx, "glGetQueryBufferObjectuiv",
991                     id, pname, GL_UNSIGNED_INT, buf, offset);
992 }
993 
994 
995 void GLAPIENTRY
_mesa_GetQueryBufferObjecti64v(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)996 _mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname,
997                                GLintptr offset)
998 {
999    struct gl_buffer_object *buf;
1000    GET_CURRENT_CONTEXT(ctx);
1001 
1002    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v");
1003    if (!buf)
1004       return;
1005 
1006    get_query_object(ctx, "glGetQueryBufferObjecti64v",
1007                     id, pname, GL_INT64_ARB, buf, offset);
1008 }
1009 
1010 
1011 void GLAPIENTRY
_mesa_GetQueryBufferObjectui64v(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)1012 _mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname,
1013                                 GLintptr offset)
1014 {
1015    struct gl_buffer_object *buf;
1016    GET_CURRENT_CONTEXT(ctx);
1017 
1018    buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v");
1019    if (!buf)
1020       return;
1021 
1022    get_query_object(ctx, "glGetQueryBufferObjectui64v",
1023                     id, pname, GL_UNSIGNED_INT64_ARB, buf, offset);
1024 }
1025 
1026 
1027 /**
1028  * Allocate/init the context state related to query objects.
1029  */
1030 void
_mesa_init_queryobj(struct gl_context * ctx)1031 _mesa_init_queryobj(struct gl_context *ctx)
1032 {
1033    ctx->Query.QueryObjects = _mesa_NewHashTable();
1034    ctx->Query.CurrentOcclusionObject = NULL;
1035 
1036    ctx->Const.QueryCounterBits.SamplesPassed = 64;
1037    ctx->Const.QueryCounterBits.TimeElapsed = 64;
1038    ctx->Const.QueryCounterBits.Timestamp = 64;
1039    ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
1040    ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
1041 
1042    ctx->Const.QueryCounterBits.VerticesSubmitted = 64;
1043    ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64;
1044    ctx->Const.QueryCounterBits.VsInvocations = 64;
1045    ctx->Const.QueryCounterBits.TessPatches = 64;
1046    ctx->Const.QueryCounterBits.TessInvocations = 64;
1047    ctx->Const.QueryCounterBits.GsInvocations = 64;
1048    ctx->Const.QueryCounterBits.GsPrimitives = 64;
1049    ctx->Const.QueryCounterBits.FsInvocations = 64;
1050    ctx->Const.QueryCounterBits.ComputeInvocations = 64;
1051    ctx->Const.QueryCounterBits.ClInPrimitives = 64;
1052    ctx->Const.QueryCounterBits.ClOutPrimitives = 64;
1053 }
1054 
1055 
1056 /**
1057  * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
1058  */
1059 static void
delete_queryobj_cb(void * data,void * userData)1060 delete_queryobj_cb(void *data, void *userData)
1061 {
1062    struct gl_query_object *q= (struct gl_query_object *) data;
1063    struct gl_context *ctx = (struct gl_context *)userData;
1064    ctx->Driver.DeleteQuery(ctx, q);
1065 }
1066 
1067 
1068 /**
1069  * Free the context state related to query objects.
1070  */
1071 void
_mesa_free_queryobj_data(struct gl_context * ctx)1072 _mesa_free_queryobj_data(struct gl_context *ctx)
1073 {
1074    _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
1075    _mesa_DeleteHashTable(ctx->Query.QueryObjects);
1076 }
1077