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 */
44static struct gl_query_object *
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 */
76static void
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 */
87static void
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 */
99static void
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 */
114static void
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 */
132void
133_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
134{
135   free(q->Label);
136   free(q);
137}
138
139
140void
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
151static struct gl_query_object **
152get_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 */
169static struct gl_query_object **
170get_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 */
260static void
261create_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
292void GLAPIENTRY
293_mesa_GenQueries(GLsizei n, GLuint *ids)
294{
295   GET_CURRENT_CONTEXT(ctx);
296   create_queries(ctx, 0, n, ids, false);
297}
298
299void GLAPIENTRY
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
325void GLAPIENTRY
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
362GLboolean GLAPIENTRY
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
383static GLboolean
384query_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
405void GLAPIENTRY
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
511void GLAPIENTRY
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
556void GLAPIENTRY
557_mesa_BeginQuery(GLenum target, GLuint id)
558{
559   _mesa_BeginQueryIndexed(target, 0, id);
560}
561
562void GLAPIENTRY
563_mesa_EndQuery(GLenum target)
564{
565   _mesa_EndQueryIndexed(target, 0);
566}
567
568void GLAPIENTRY
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
642void GLAPIENTRY
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
782void GLAPIENTRY
783_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
784{
785   _mesa_GetQueryIndexediv(target, 0, pname, params);
786}
787
788static void
789get_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:
878invalid_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
912void GLAPIENTRY
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
922void GLAPIENTRY
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 */
936void GLAPIENTRY
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 */
950void GLAPIENTRY
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 */
963void GLAPIENTRY
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
979void GLAPIENTRY
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
995void GLAPIENTRY
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
1011void GLAPIENTRY
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 */
1030void
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 */
1059static void
1060delete_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 */
1071void
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