queryobj.c revision 01e04c3f
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
35
36/**
37 * Allocate a new query object.  This is a fallback routine called via
38 * ctx->Driver.NewQueryObject().
39 * \param ctx - rendering context
40 * \param id - the new object's ID
41 * \return pointer to new query_object object or NULL if out of memory.
42 */
43static struct gl_query_object *
44_mesa_new_query_object(struct gl_context *ctx, GLuint id)
45{
46   struct gl_query_object *q = CALLOC_STRUCT(gl_query_object);
47   (void) ctx;
48   if (q) {
49      q->Id = id;
50      q->Result = 0;
51      q->Active = GL_FALSE;
52
53      /* This is to satisfy the language of the specification: "In the initial
54       * state of a query object, the result is available" (OpenGL 3.1 §
55       * 2.13).
56       */
57      q->Ready = GL_TRUE;
58
59      /* OpenGL 3.1 § 2.13 says about GenQueries, "These names are marked as
60       * used, but no object is associated with them until the first time they
61       * are used by BeginQuery." Since our implementation actually does
62       * allocate an object at this point, use a flag to indicate that this
63       * object has not yet been bound so should not be considered a query.
64       */
65      q->EverBound = GL_FALSE;
66   }
67   return q;
68}
69
70
71/**
72 * Begin a query.  Software driver fallback.
73 * Called via ctx->Driver.BeginQuery().
74 */
75static void
76_mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
77{
78   ctx->NewState |= _NEW_DEPTH; /* for swrast */
79}
80
81
82/**
83 * End a query.  Software driver fallback.
84 * Called via ctx->Driver.EndQuery().
85 */
86static void
87_mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
88{
89   ctx->NewState |= _NEW_DEPTH; /* for swrast */
90   q->Ready = GL_TRUE;
91}
92
93
94/**
95 * Wait for query to complete.  Software driver fallback.
96 * Called via ctx->Driver.WaitQuery().
97 */
98static void
99_mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
100{
101   /* For software drivers, _mesa_end_query() should have completed the query.
102    * For real hardware, implement a proper WaitQuery() driver function,
103    * which may require issuing a flush.
104    */
105   assert(q->Ready);
106}
107
108
109/**
110 * Check if a query results are ready.  Software driver fallback.
111 * Called via ctx->Driver.CheckQuery().
112 */
113static void
114_mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
115{
116   /* No-op for sw rendering.
117    * HW drivers may need to flush at this time.
118    */
119}
120
121
122/**
123 * Delete a query object.  Called via ctx->Driver.DeleteQuery().
124 * Not removed from hash table here.
125 */
126static void
127_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
128{
129   free(q->Label);
130   free(q);
131}
132
133
134void
135_mesa_init_query_object_functions(struct dd_function_table *driver)
136{
137   driver->NewQueryObject = _mesa_new_query_object;
138   driver->DeleteQuery = _mesa_delete_query;
139   driver->BeginQuery = _mesa_begin_query;
140   driver->EndQuery = _mesa_end_query;
141   driver->WaitQuery = _mesa_wait_query;
142   driver->CheckQuery = _mesa_check_query;
143}
144
145static struct gl_query_object **
146get_pipe_stats_binding_point(struct gl_context *ctx,
147                             GLenum target)
148{
149   const int which = target - GL_VERTICES_SUBMITTED_ARB;
150   assert(which < MAX_PIPELINE_STATISTICS);
151
152   if (!_mesa_is_desktop_gl(ctx) ||
153       !ctx->Extensions.ARB_pipeline_statistics_query)
154      return NULL;
155
156   return &ctx->Query.pipeline_stats[which];
157}
158
159/**
160 * Return pointer to the query object binding point for the given target and
161 * index.
162 * \return NULL if invalid target, else the address of binding point
163 */
164static struct gl_query_object **
165get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
166{
167
168   /* From GL_EXT_occlusion_query_boolean spec:
169    *
170    *    "Accepted by the <target> parameter of BeginQueryEXT, EndQueryEXT,
171    *    and GetQueryivEXT:
172    *
173    *   ANY_SAMPLES_PASSED_EXT                         0x8C2F
174    *   ANY_SAMPLES_PASSED_CONSERVATIVE_EXT            0x8D6A"
175    */
176   if ((_mesa_is_gles(ctx) && ctx->Version == 20) &&
177       (target != GL_ANY_SAMPLES_PASSED &&
178        target != GL_ANY_SAMPLES_PASSED_CONSERVATIVE))
179      return NULL;
180
181   switch (target) {
182   case GL_SAMPLES_PASSED_ARB:
183      if (ctx->Extensions.ARB_occlusion_query)
184         return &ctx->Query.CurrentOcclusionObject;
185      else
186         return NULL;
187   case GL_ANY_SAMPLES_PASSED:
188      if (ctx->Extensions.ARB_occlusion_query2)
189         return &ctx->Query.CurrentOcclusionObject;
190      else
191         return NULL;
192   case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
193      if (ctx->Extensions.ARB_ES3_compatibility
194          || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
195         return &ctx->Query.CurrentOcclusionObject;
196      else
197         return NULL;
198   case GL_TIME_ELAPSED_EXT:
199      if (ctx->Extensions.EXT_timer_query)
200         return &ctx->Query.CurrentTimerObject;
201      else
202         return NULL;
203   case GL_PRIMITIVES_GENERATED:
204      if (ctx->Extensions.EXT_transform_feedback)
205         return &ctx->Query.PrimitivesGenerated[index];
206      else
207         return NULL;
208   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
209      if (ctx->Extensions.EXT_transform_feedback)
210         return &ctx->Query.PrimitivesWritten[index];
211      else
212         return NULL;
213   case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
214      if (ctx->Extensions.ARB_transform_feedback_overflow_query)
215         return &ctx->Query.TransformFeedbackOverflow[index];
216      else
217         return NULL;
218   case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
219      if (ctx->Extensions.ARB_transform_feedback_overflow_query)
220         return &ctx->Query.TransformFeedbackOverflowAny;
221      else
222         return NULL;
223
224   case GL_VERTICES_SUBMITTED_ARB:
225   case GL_PRIMITIVES_SUBMITTED_ARB:
226   case GL_VERTEX_SHADER_INVOCATIONS_ARB:
227   case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
228   case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
229   case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
230         return get_pipe_stats_binding_point(ctx, target);
231
232   case GL_GEOMETRY_SHADER_INVOCATIONS:
233      /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */
234      target = GL_VERTICES_SUBMITTED_ARB + MAX_PIPELINE_STATISTICS - 1;
235      /* fallthrough */
236   case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
237      if (_mesa_has_geometry_shaders(ctx))
238         return get_pipe_stats_binding_point(ctx, target);
239      else
240         return NULL;
241
242   case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
243   case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
244      if (_mesa_has_tessellation(ctx))
245         return get_pipe_stats_binding_point(ctx, target);
246      else
247         return NULL;
248
249   case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
250      if (_mesa_has_compute_shaders(ctx))
251         return get_pipe_stats_binding_point(ctx, target);
252      else
253         return NULL;
254
255   default:
256      return NULL;
257   }
258}
259
260/**
261 * Create $n query objects and store them in *ids. Make them of type $target
262 * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries().
263 */
264static void
265create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids,
266               bool dsa)
267{
268   const char *func = dsa ? "glGenQueries" : "glCreateQueries";
269   GLuint first;
270
271   if (MESA_VERBOSE & VERBOSE_API)
272      _mesa_debug(ctx, "%s(%d)\n", func, n);
273
274   if (n < 0) {
275      _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
276      return;
277   }
278
279   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
280   if (first) {
281      GLsizei i;
282      for (i = 0; i < n; i++) {
283         struct gl_query_object *q
284            = ctx->Driver.NewQueryObject(ctx, first + i);
285         if (!q) {
286            _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
287            return;
288         } else if (dsa) {
289            /* Do the equivalent of binding the buffer with a target */
290            q->Target = target;
291            q->EverBound = GL_TRUE;
292         }
293         ids[i] = first + i;
294         _mesa_HashInsertLocked(ctx->Query.QueryObjects, first + i, q);
295      }
296   }
297}
298
299void GLAPIENTRY
300_mesa_GenQueries(GLsizei n, GLuint *ids)
301{
302   GET_CURRENT_CONTEXT(ctx);
303   create_queries(ctx, 0, n, ids, false);
304}
305
306void GLAPIENTRY
307_mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids)
308{
309   GET_CURRENT_CONTEXT(ctx);
310
311   switch (target) {
312   case GL_SAMPLES_PASSED:
313   case GL_ANY_SAMPLES_PASSED:
314   case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
315   case GL_TIME_ELAPSED:
316   case GL_TIMESTAMP:
317   case GL_PRIMITIVES_GENERATED:
318   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
319   case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
320   case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
321      break;
322   default:
323      _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)",
324                  _mesa_enum_to_string(target));
325      return;
326   }
327
328   create_queries(ctx, target, n, ids, true);
329}
330
331
332void GLAPIENTRY
333_mesa_DeleteQueries(GLsizei n, const GLuint *ids)
334{
335   GLint i;
336   GET_CURRENT_CONTEXT(ctx);
337   FLUSH_VERTICES(ctx, 0);
338
339   if (MESA_VERBOSE & VERBOSE_API)
340      _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
341
342   if (n < 0) {
343      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
344      return;
345   }
346
347   for (i = 0; i < n; i++) {
348      if (ids[i] > 0) {
349         struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
350         if (q) {
351            if (q->Active) {
352               struct gl_query_object **bindpt;
353               bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
354               assert(bindpt); /* Should be non-null for active q. */
355               if (bindpt) {
356                  *bindpt = NULL;
357               }
358               q->Active = GL_FALSE;
359               ctx->Driver.EndQuery(ctx, q);
360            }
361            _mesa_HashRemoveLocked(ctx->Query.QueryObjects, ids[i]);
362            ctx->Driver.DeleteQuery(ctx, q);
363         }
364      }
365   }
366}
367
368
369GLboolean GLAPIENTRY
370_mesa_IsQuery(GLuint id)
371{
372   struct gl_query_object *q;
373
374   GET_CURRENT_CONTEXT(ctx);
375   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
376
377   if (MESA_VERBOSE & VERBOSE_API)
378      _mesa_debug(ctx, "glIsQuery(%u)\n", id);
379
380   if (id == 0)
381      return GL_FALSE;
382
383   q = _mesa_lookup_query_object(ctx, id);
384   if (q == NULL)
385      return GL_FALSE;
386
387   return q->EverBound;
388}
389
390static GLboolean
391query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
392{
393   switch (target) {
394   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
395   case GL_PRIMITIVES_GENERATED:
396   case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB:
397      if (index >= ctx->Const.MaxVertexStreams) {
398         _mesa_error(ctx, GL_INVALID_VALUE,
399                     "glBeginQueryIndexed(index>=MaxVertexStreams)");
400         return GL_FALSE;
401      }
402      break;
403   default:
404      if (index > 0) {
405         _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
406         return GL_FALSE;
407      }
408   }
409   return GL_TRUE;
410}
411
412void GLAPIENTRY
413_mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
414{
415   struct gl_query_object *q, **bindpt;
416   GET_CURRENT_CONTEXT(ctx);
417
418   if (MESA_VERBOSE & VERBOSE_API)
419      _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
420                  _mesa_enum_to_string(target), index, id);
421
422   if (!query_error_check_index(ctx, target, index))
423      return;
424
425   FLUSH_VERTICES(ctx, 0);
426
427   bindpt = get_query_binding_point(ctx, target, index);
428   if (!bindpt) {
429      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
430      return;
431   }
432
433   /* From the GL_ARB_occlusion_query spec:
434    *
435    *     "If BeginQueryARB is called while another query is already in
436    *      progress with the same target, an INVALID_OPERATION error is
437    *      generated."
438    */
439   if (*bindpt) {
440      _mesa_error(ctx, GL_INVALID_OPERATION,
441                  "glBeginQuery{Indexed}(target=%s is active)",
442                  _mesa_enum_to_string(target));
443      return;
444   }
445
446   if (id == 0) {
447      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
448      return;
449   }
450
451   q = _mesa_lookup_query_object(ctx, id);
452   if (!q) {
453      if (ctx->API != API_OPENGL_COMPAT) {
454         _mesa_error(ctx, GL_INVALID_OPERATION,
455                     "glBeginQuery{Indexed}(non-gen name)");
456         return;
457      } else {
458         /* create new object */
459         q = ctx->Driver.NewQueryObject(ctx, id);
460         if (!q) {
461            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
462            return;
463         }
464         _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q);
465      }
466   }
467   else {
468      /* pre-existing object */
469      if (q->Active) {
470         _mesa_error(ctx, GL_INVALID_OPERATION,
471                     "glBeginQuery{Indexed}(query already active)");
472         return;
473      }
474
475      /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4
476       * spec states:
477       *
478       *     "BeginQuery generates an INVALID_OPERATION error if any of the
479       *      following conditions hold: [...] id is the name of an
480       *      existing query object whose type does not match target; [...]
481       *
482       * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY
483       * OBJECTS AND ASYNCHRONOUS QUERIES, page 43.
484       */
485      if (q->EverBound && q->Target != target) {
486         _mesa_error(ctx, GL_INVALID_OPERATION,
487                     "glBeginQuery{Indexed}(target mismatch)");
488         return;
489      }
490   }
491
492   /* This possibly changes the target of a buffer allocated by
493    * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
494    * the following:
495    *
496    * "CreateQueries adds a <target>, so strictly speaking the <target>
497    * command isn't needed for BeginQuery/EndQuery, but in the end, this also
498    * isn't a selector, so we decided not to change it."
499    *
500    * Updating the target of the query object should be acceptable, so let's
501    * do that.
502    */
503
504   q->Target = target;
505   q->Active = GL_TRUE;
506   q->Result = 0;
507   q->Ready = GL_FALSE;
508   q->EverBound = GL_TRUE;
509   q->Stream = index;
510
511   /* XXX should probably refcount query objects */
512   *bindpt = q;
513
514   ctx->Driver.BeginQuery(ctx, q);
515}
516
517
518void GLAPIENTRY
519_mesa_EndQueryIndexed(GLenum target, GLuint index)
520{
521   struct gl_query_object *q, **bindpt;
522   GET_CURRENT_CONTEXT(ctx);
523
524   if (MESA_VERBOSE & VERBOSE_API)
525      _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
526                  _mesa_enum_to_string(target), index);
527
528   if (!query_error_check_index(ctx, target, index))
529      return;
530
531   FLUSH_VERTICES(ctx, 0);
532
533   bindpt = get_query_binding_point(ctx, target, index);
534   if (!bindpt) {
535      _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
536      return;
537   }
538
539   /* XXX should probably refcount query objects */
540   q = *bindpt;
541
542   /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
543   if (q && q->Target != target) {
544      _mesa_error(ctx, GL_INVALID_OPERATION,
545                  "glEndQuery(target=%s with active query of target %s)",
546                  _mesa_enum_to_string(target),
547                  _mesa_enum_to_string(q->Target));
548      return;
549   }
550
551   *bindpt = NULL;
552
553   if (!q || !q->Active) {
554      _mesa_error(ctx, GL_INVALID_OPERATION,
555                  "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
556      return;
557   }
558
559   q->Active = GL_FALSE;
560   ctx->Driver.EndQuery(ctx, q);
561}
562
563void GLAPIENTRY
564_mesa_BeginQuery(GLenum target, GLuint id)
565{
566   _mesa_BeginQueryIndexed(target, 0, id);
567}
568
569void GLAPIENTRY
570_mesa_EndQuery(GLenum target)
571{
572   _mesa_EndQueryIndexed(target, 0);
573}
574
575void GLAPIENTRY
576_mesa_QueryCounter(GLuint id, GLenum target)
577{
578   struct gl_query_object *q;
579   GET_CURRENT_CONTEXT(ctx);
580
581   if (MESA_VERBOSE & VERBOSE_API)
582      _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
583                  _mesa_enum_to_string(target));
584
585   /* error checking */
586   if (target != GL_TIMESTAMP) {
587      _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
588      return;
589   }
590
591   if (id == 0) {
592      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
593      return;
594   }
595
596   q = _mesa_lookup_query_object(ctx, id);
597   if (!q) {
598      /* XXX the Core profile should throw INVALID_OPERATION here */
599
600      /* create new object */
601      q = ctx->Driver.NewQueryObject(ctx, id);
602      if (!q) {
603         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
604         return;
605      }
606      _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q);
607   }
608   else {
609      if (q->Target && q->Target != GL_TIMESTAMP) {
610         _mesa_error(ctx, GL_INVALID_OPERATION,
611                     "glQueryCounter(id has an invalid target)");
612         return;
613      }
614   }
615
616   if (q->Active) {
617      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
618      return;
619   }
620
621   /* This possibly changes the target of a buffer allocated by
622    * CreateQueries. Issue 39) in the ARB_direct_state_access extension states
623    * the following:
624    *
625    * "CreateQueries adds a <target>, so strictly speaking the <target>
626    * command isn't needed for BeginQuery/EndQuery, but in the end, this also
627    * isn't a selector, so we decided not to change it."
628    *
629    * Updating the target of the query object should be acceptable, so let's
630    * do that.
631    */
632
633   q->Target = target;
634   q->Result = 0;
635   q->Ready = GL_FALSE;
636   q->EverBound = GL_TRUE;
637
638   if (ctx->Driver.QueryCounter) {
639      ctx->Driver.QueryCounter(ctx, q);
640   } else {
641      /* QueryCounter is implemented using EndQuery without BeginQuery
642       * in drivers. This is actually Direct3D and Gallium convention.
643       */
644      ctx->Driver.EndQuery(ctx, q);
645   }
646}
647
648
649void GLAPIENTRY
650_mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
651                        GLint *params)
652{
653   struct gl_query_object *q = NULL, **bindpt = NULL;
654   GET_CURRENT_CONTEXT(ctx);
655
656   if (MESA_VERBOSE & VERBOSE_API)
657      _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
658                  _mesa_enum_to_string(target),
659                  index,
660                  _mesa_enum_to_string(pname));
661
662   if (!query_error_check_index(ctx, target, index))
663      return;
664
665   /* From the GL_EXT_occlusion_query_boolean spec:
666    *
667    * "The error INVALID_ENUM is generated if GetQueryivEXT is called where
668    * <pname> is not CURRENT_QUERY_EXT."
669    *
670    * Same rule is present also in ES 3.2 spec.
671    */
672   if (_mesa_is_gles(ctx) && pname != GL_CURRENT_QUERY) {
673      _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivEXT(%s)",
674                  _mesa_enum_to_string(pname));
675      return;
676   }
677
678   if (target == GL_TIMESTAMP) {
679      if (!ctx->Extensions.ARB_timer_query) {
680         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
681         return;
682      }
683   }
684   else {
685      bindpt = get_query_binding_point(ctx, target, index);
686      if (!bindpt) {
687         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
688         return;
689      }
690
691      q = *bindpt;
692   }
693
694   switch (pname) {
695      case GL_QUERY_COUNTER_BITS_ARB:
696         switch (target) {
697         case GL_SAMPLES_PASSED:
698            *params = ctx->Const.QueryCounterBits.SamplesPassed;
699            break;
700         case GL_ANY_SAMPLES_PASSED:
701         case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
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
778void GLAPIENTRY
779_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
780{
781   _mesa_GetQueryIndexediv(target, 0, pname, params);
782}
783
784static void
785get_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         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func);
827         return;
828      }
829      if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) {
830         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func);
831         return;
832      }
833
834      if (offset < 0) {
835         _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func);
836         return;
837      }
838
839      switch (pname) {
840      case GL_QUERY_RESULT:
841      case GL_QUERY_RESULT_NO_WAIT:
842      case GL_QUERY_RESULT_AVAILABLE:
843      case GL_QUERY_TARGET:
844         ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype);
845         return;
846      }
847
848      /* fall through to get error below */
849   }
850
851   switch (pname) {
852   case GL_QUERY_RESULT:
853      if (!q->Ready)
854         ctx->Driver.WaitQuery(ctx, q);
855      value = q->Result;
856      break;
857   case GL_QUERY_RESULT_NO_WAIT:
858      if (!ctx->Extensions.ARB_query_buffer_object)
859         goto invalid_enum;
860      ctx->Driver.CheckQuery(ctx, q);
861      if (!q->Ready)
862         return;
863      value = q->Result;
864      break;
865   case GL_QUERY_RESULT_AVAILABLE:
866      if (!q->Ready)
867         ctx->Driver.CheckQuery(ctx, q);
868      value = q->Ready;
869      break;
870   case GL_QUERY_TARGET:
871      value = q->Target;
872      break;
873   default:
874invalid_enum:
875      _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)",
876                  func, _mesa_enum_to_string(pname));
877      return;
878   }
879
880   switch (ptype) {
881   case GL_INT: {
882      GLint *param = (GLint *)offset;
883      if (value > 0x7fffffff)
884         *param = 0x7fffffff;
885      else
886         *param = value;
887      break;
888   }
889   case GL_UNSIGNED_INT: {
890      GLuint *param = (GLuint *)offset;
891      if (value > 0xffffffff)
892         *param = 0xffffffff;
893      else
894         *param = value;
895      break;
896   }
897   case GL_INT64_ARB:
898   case GL_UNSIGNED_INT64_ARB: {
899      GLuint64EXT *param = (GLuint64EXT *)offset;
900      *param = value;
901      break;
902   }
903   default:
904      unreachable("unexpected ptype");
905   }
906}
907
908void GLAPIENTRY
909_mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
910{
911   GET_CURRENT_CONTEXT(ctx);
912
913   get_query_object(ctx, "glGetQueryObjectiv",
914                    id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params);
915}
916
917
918void GLAPIENTRY
919_mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
920{
921   GET_CURRENT_CONTEXT(ctx);
922
923   get_query_object(ctx, "glGetQueryObjectuiv",
924                    id, pname, GL_UNSIGNED_INT,
925                    ctx->QueryBuffer, (intptr_t)params);
926}
927
928
929/**
930 * New with GL_EXT_timer_query
931 */
932void GLAPIENTRY
933_mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
934{
935   GET_CURRENT_CONTEXT(ctx);
936
937   get_query_object(ctx, "glGetQueryObjecti64v",
938                    id, pname, GL_INT64_ARB,
939                    ctx->QueryBuffer, (intptr_t)params);
940}
941
942
943/**
944 * New with GL_EXT_timer_query
945 */
946void GLAPIENTRY
947_mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
948{
949   GET_CURRENT_CONTEXT(ctx);
950
951   get_query_object(ctx, "glGetQueryObjectui64v",
952                    id, pname, GL_UNSIGNED_INT64_ARB,
953                    ctx->QueryBuffer, (intptr_t)params);
954}
955
956/**
957 * New with GL_ARB_query_buffer_object
958 */
959void GLAPIENTRY
960_mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname,
961                             GLintptr offset)
962{
963   struct gl_buffer_object *buf;
964   GET_CURRENT_CONTEXT(ctx);
965
966   buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv");
967   if (!buf)
968      return;
969
970   get_query_object(ctx, "glGetQueryBufferObjectiv",
971                    id, pname, GL_INT, buf, offset);
972}
973
974
975void GLAPIENTRY
976_mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname,
977                              GLintptr offset)
978{
979   struct gl_buffer_object *buf;
980   GET_CURRENT_CONTEXT(ctx);
981
982   buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv");
983   if (!buf)
984      return;
985
986   get_query_object(ctx, "glGetQueryBufferObjectuiv",
987                    id, pname, GL_UNSIGNED_INT, buf, offset);
988}
989
990
991void GLAPIENTRY
992_mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname,
993                               GLintptr offset)
994{
995   struct gl_buffer_object *buf;
996   GET_CURRENT_CONTEXT(ctx);
997
998   buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v");
999   if (!buf)
1000      return;
1001
1002   get_query_object(ctx, "glGetQueryBufferObjecti64v",
1003                    id, pname, GL_INT64_ARB, buf, offset);
1004}
1005
1006
1007void GLAPIENTRY
1008_mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname,
1009                                GLintptr offset)
1010{
1011   struct gl_buffer_object *buf;
1012   GET_CURRENT_CONTEXT(ctx);
1013
1014   buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v");
1015   if (!buf)
1016      return;
1017
1018   get_query_object(ctx, "glGetQueryBufferObjectui64v",
1019                    id, pname, GL_UNSIGNED_INT64_ARB, buf, offset);
1020}
1021
1022
1023/**
1024 * Allocate/init the context state related to query objects.
1025 */
1026void
1027_mesa_init_queryobj(struct gl_context *ctx)
1028{
1029   ctx->Query.QueryObjects = _mesa_NewHashTable();
1030   ctx->Query.CurrentOcclusionObject = NULL;
1031
1032   ctx->Const.QueryCounterBits.SamplesPassed = 64;
1033   ctx->Const.QueryCounterBits.TimeElapsed = 64;
1034   ctx->Const.QueryCounterBits.Timestamp = 64;
1035   ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
1036   ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
1037
1038   ctx->Const.QueryCounterBits.VerticesSubmitted = 64;
1039   ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64;
1040   ctx->Const.QueryCounterBits.VsInvocations = 64;
1041   ctx->Const.QueryCounterBits.TessPatches = 64;
1042   ctx->Const.QueryCounterBits.TessInvocations = 64;
1043   ctx->Const.QueryCounterBits.GsInvocations = 64;
1044   ctx->Const.QueryCounterBits.GsPrimitives = 64;
1045   ctx->Const.QueryCounterBits.FsInvocations = 64;
1046   ctx->Const.QueryCounterBits.ComputeInvocations = 64;
1047   ctx->Const.QueryCounterBits.ClInPrimitives = 64;
1048   ctx->Const.QueryCounterBits.ClOutPrimitives = 64;
1049}
1050
1051
1052/**
1053 * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
1054 */
1055static void
1056delete_queryobj_cb(GLuint id, void *data, void *userData)
1057{
1058   struct gl_query_object *q= (struct gl_query_object *) data;
1059   struct gl_context *ctx = (struct gl_context *)userData;
1060   ctx->Driver.DeleteQuery(ctx, q);
1061}
1062
1063
1064/**
1065 * Free the context state related to query objects.
1066 */
1067void
1068_mesa_free_queryobj_data(struct gl_context *ctx)
1069{
1070   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
1071   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
1072}
1073