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