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