queryobj.c revision 848b8605
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 "glheader.h"
27#include "context.h"
28#include "enums.h"
29#include "hash.h"
30#include "imports.h"
31#include "queryobj.h"
32#include "mtypes.h"
33#include "main/dispatch.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
145
146/**
147 * Return pointer to the query object binding point for the given target and
148 * index.
149 * \return NULL if invalid target, else the address of binding point
150 */
151static struct gl_query_object **
152get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
153{
154   switch (target) {
155   case GL_SAMPLES_PASSED_ARB:
156      if (ctx->Extensions.ARB_occlusion_query)
157         return &ctx->Query.CurrentOcclusionObject;
158      else
159         return NULL;
160   case GL_ANY_SAMPLES_PASSED:
161      if (ctx->Extensions.ARB_occlusion_query2)
162         return &ctx->Query.CurrentOcclusionObject;
163      else
164         return NULL;
165   case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
166      if (ctx->Extensions.ARB_ES3_compatibility
167          || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
168         return &ctx->Query.CurrentOcclusionObject;
169      else
170         return NULL;
171   case GL_TIME_ELAPSED_EXT:
172      if (ctx->Extensions.EXT_timer_query)
173         return &ctx->Query.CurrentTimerObject;
174      else
175         return NULL;
176   case GL_PRIMITIVES_GENERATED:
177      if (ctx->Extensions.EXT_transform_feedback)
178         return &ctx->Query.PrimitivesGenerated[index];
179      else
180         return NULL;
181   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
182      if (ctx->Extensions.EXT_transform_feedback)
183         return &ctx->Query.PrimitivesWritten[index];
184      else
185         return NULL;
186   default:
187      return NULL;
188   }
189}
190
191
192void GLAPIENTRY
193_mesa_GenQueries(GLsizei n, GLuint *ids)
194{
195   GLuint first;
196   GET_CURRENT_CONTEXT(ctx);
197
198   if (MESA_VERBOSE & VERBOSE_API)
199      _mesa_debug(ctx, "glGenQueries(%d)\n", n);
200
201   if (n < 0) {
202      _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
203      return;
204   }
205
206   first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
207   if (first) {
208      GLsizei i;
209      for (i = 0; i < n; i++) {
210         struct gl_query_object *q
211            = ctx->Driver.NewQueryObject(ctx, first + i);
212         if (!q) {
213            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
214            return;
215         }
216         ids[i] = first + i;
217         _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
218      }
219   }
220}
221
222
223void GLAPIENTRY
224_mesa_DeleteQueries(GLsizei n, const GLuint *ids)
225{
226   GLint i;
227   GET_CURRENT_CONTEXT(ctx);
228   FLUSH_VERTICES(ctx, 0);
229
230   if (MESA_VERBOSE & VERBOSE_API)
231      _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
232
233   if (n < 0) {
234      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
235      return;
236   }
237
238   for (i = 0; i < n; i++) {
239      if (ids[i] > 0) {
240         struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
241         if (q) {
242            if (q->Active) {
243               struct gl_query_object **bindpt;
244               bindpt = get_query_binding_point(ctx, q->Target, q->Stream);
245               assert(bindpt); /* Should be non-null for active q. */
246               if (bindpt) {
247                  *bindpt = NULL;
248               }
249               q->Active = GL_FALSE;
250               ctx->Driver.EndQuery(ctx, q);
251            }
252            _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
253            ctx->Driver.DeleteQuery(ctx, q);
254         }
255      }
256   }
257}
258
259
260GLboolean GLAPIENTRY
261_mesa_IsQuery(GLuint id)
262{
263   struct gl_query_object *q;
264
265   GET_CURRENT_CONTEXT(ctx);
266   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
267
268   if (MESA_VERBOSE & VERBOSE_API)
269      _mesa_debug(ctx, "glIsQuery(%u)\n", id);
270
271   if (id == 0)
272      return GL_FALSE;
273
274   q = _mesa_lookup_query_object(ctx, id);
275   if (q == NULL)
276      return GL_FALSE;
277
278   return q->EverBound;
279}
280
281static GLboolean
282query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
283{
284   switch (target) {
285   case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
286   case GL_PRIMITIVES_GENERATED:
287      if (index >= ctx->Const.MaxVertexStreams) {
288         _mesa_error(ctx, GL_INVALID_VALUE,
289                     "glBeginQueryIndexed(index>=MaxVertexStreams)");
290         return GL_FALSE;
291      }
292      break;
293   default:
294      if (index > 0) {
295         _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
296         return GL_FALSE;
297      }
298   }
299   return GL_TRUE;
300}
301
302void GLAPIENTRY
303_mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
304{
305   struct gl_query_object *q, **bindpt;
306   GET_CURRENT_CONTEXT(ctx);
307
308   if (MESA_VERBOSE & VERBOSE_API)
309      _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
310                  _mesa_lookup_enum_by_nr(target), index, id);
311
312   if (!query_error_check_index(ctx, target, index))
313      return;
314
315   FLUSH_VERTICES(ctx, 0);
316
317   bindpt = get_query_binding_point(ctx, target, index);
318   if (!bindpt) {
319      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
320      return;
321   }
322
323   /* From the GL_ARB_occlusion_query spec:
324    *
325    *     "If BeginQueryARB is called while another query is already in
326    *      progress with the same target, an INVALID_OPERATION error is
327    *      generated."
328    */
329   if (*bindpt) {
330      _mesa_error(ctx, GL_INVALID_OPERATION,
331                  "glBeginQuery{Indexed}(target=%s is active)",
332                  _mesa_lookup_enum_by_nr(target));
333      return;
334   }
335
336   if (id == 0) {
337      _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
338      return;
339   }
340
341   q = _mesa_lookup_query_object(ctx, id);
342   if (!q) {
343      if (ctx->API != API_OPENGL_COMPAT) {
344         _mesa_error(ctx, GL_INVALID_OPERATION,
345                     "glBeginQuery{Indexed}(non-gen name)");
346         return;
347      } else {
348         /* create new object */
349         q = ctx->Driver.NewQueryObject(ctx, id);
350         if (!q) {
351            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
352            return;
353         }
354         _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
355      }
356   }
357   else {
358      /* pre-existing object */
359      if (q->Active) {
360         _mesa_error(ctx, GL_INVALID_OPERATION,
361                     "glBeginQuery{Indexed}(query already active)");
362         return;
363      }
364   }
365
366   q->Target = target;
367   q->Active = GL_TRUE;
368   q->Result = 0;
369   q->Ready = GL_FALSE;
370   q->EverBound = GL_TRUE;
371   q->Stream = index;
372
373   /* XXX should probably refcount query objects */
374   *bindpt = q;
375
376   ctx->Driver.BeginQuery(ctx, q);
377}
378
379
380void GLAPIENTRY
381_mesa_EndQueryIndexed(GLenum target, GLuint index)
382{
383   struct gl_query_object *q, **bindpt;
384   GET_CURRENT_CONTEXT(ctx);
385
386   if (MESA_VERBOSE & VERBOSE_API)
387      _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
388                  _mesa_lookup_enum_by_nr(target), index);
389
390   if (!query_error_check_index(ctx, target, index))
391      return;
392
393   FLUSH_VERTICES(ctx, 0);
394
395   bindpt = get_query_binding_point(ctx, target, index);
396   if (!bindpt) {
397      _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
398      return;
399   }
400
401   /* XXX should probably refcount query objects */
402   q = *bindpt;
403
404   /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
405   if (q && q->Target != target) {
406      _mesa_error(ctx, GL_INVALID_OPERATION,
407                  "glEndQuery(target=%s with active query of target %s)",
408                  _mesa_lookup_enum_by_nr(target),
409                  _mesa_lookup_enum_by_nr(q->Target));
410      return;
411   }
412
413   *bindpt = NULL;
414
415   if (!q || !q->Active) {
416      _mesa_error(ctx, GL_INVALID_OPERATION,
417                  "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
418      return;
419   }
420
421   q->Active = GL_FALSE;
422   ctx->Driver.EndQuery(ctx, q);
423}
424
425void GLAPIENTRY
426_mesa_BeginQuery(GLenum target, GLuint id)
427{
428   _mesa_BeginQueryIndexed(target, 0, id);
429}
430
431void GLAPIENTRY
432_mesa_EndQuery(GLenum target)
433{
434   _mesa_EndQueryIndexed(target, 0);
435}
436
437void GLAPIENTRY
438_mesa_QueryCounter(GLuint id, GLenum target)
439{
440   struct gl_query_object *q;
441   GET_CURRENT_CONTEXT(ctx);
442
443   if (MESA_VERBOSE & VERBOSE_API)
444      _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
445                  _mesa_lookup_enum_by_nr(target));
446
447   /* error checking */
448   if (target != GL_TIMESTAMP) {
449      _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
450      return;
451   }
452
453   if (id == 0) {
454      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
455      return;
456   }
457
458   q = _mesa_lookup_query_object(ctx, id);
459   if (!q) {
460      /* XXX the Core profile should throw INVALID_OPERATION here */
461
462      /* create new object */
463      q = ctx->Driver.NewQueryObject(ctx, id);
464      if (!q) {
465         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
466         return;
467      }
468      _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
469   }
470   else {
471      if (q->Target && q->Target != GL_TIMESTAMP) {
472         _mesa_error(ctx, GL_INVALID_OPERATION,
473                     "glQueryCounter(id has an invalid target)");
474         return;
475      }
476   }
477
478   if (q->Active) {
479      _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
480      return;
481   }
482
483   q->Target = target;
484   q->Result = 0;
485   q->Ready = GL_FALSE;
486   q->EverBound = GL_TRUE;
487
488   if (ctx->Driver.QueryCounter) {
489      ctx->Driver.QueryCounter(ctx, q);
490   } else {
491      /* QueryCounter is implemented using EndQuery without BeginQuery
492       * in drivers. This is actually Direct3D and Gallium convention.
493       */
494      ctx->Driver.EndQuery(ctx, q);
495   }
496}
497
498
499void GLAPIENTRY
500_mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
501                        GLint *params)
502{
503   struct gl_query_object *q = NULL, **bindpt = NULL;
504   GET_CURRENT_CONTEXT(ctx);
505
506   if (MESA_VERBOSE & VERBOSE_API)
507      _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
508                  _mesa_lookup_enum_by_nr(target),
509                  index,
510                  _mesa_lookup_enum_by_nr(pname));
511
512   if (!query_error_check_index(ctx, target, index))
513      return;
514
515   if (target == GL_TIMESTAMP) {
516      if (!ctx->Extensions.ARB_timer_query) {
517         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
518         return;
519      }
520   }
521   else {
522      bindpt = get_query_binding_point(ctx, target, index);
523      if (!bindpt) {
524         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
525         return;
526      }
527
528      q = *bindpt;
529   }
530
531   switch (pname) {
532      case GL_QUERY_COUNTER_BITS_ARB:
533         switch (target) {
534         case GL_SAMPLES_PASSED:
535            *params = ctx->Const.QueryCounterBits.SamplesPassed;
536            break;
537         case GL_ANY_SAMPLES_PASSED:
538            /* The minimum value of this is 1 if it's nonzero, and the value
539             * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
540             * bits.
541             */
542            *params = 1;
543            break;
544         case GL_TIME_ELAPSED:
545            *params = ctx->Const.QueryCounterBits.TimeElapsed;
546            break;
547         case GL_TIMESTAMP:
548            *params = ctx->Const.QueryCounterBits.Timestamp;
549            break;
550         case GL_PRIMITIVES_GENERATED:
551            *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
552            break;
553         case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
554            *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
555            break;
556         default:
557            _mesa_problem(ctx,
558                          "Unknown target in glGetQueryIndexediv(target = %s)",
559                          _mesa_lookup_enum_by_nr(target));
560            *params = 0;
561            break;
562         }
563         break;
564      case GL_CURRENT_QUERY_ARB:
565         *params = (q && q->Target == target) ? q->Id : 0;
566         break;
567      default:
568         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
569         return;
570   }
571}
572
573void GLAPIENTRY
574_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
575{
576   _mesa_GetQueryIndexediv(target, 0, pname, params);
577}
578
579void GLAPIENTRY
580_mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
581{
582   struct gl_query_object *q = NULL;
583   GET_CURRENT_CONTEXT(ctx);
584
585   if (MESA_VERBOSE & VERBOSE_API)
586      _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id,
587                  _mesa_lookup_enum_by_nr(pname));
588
589   if (id)
590      q = _mesa_lookup_query_object(ctx, id);
591
592   if (!q || q->Active) {
593      _mesa_error(ctx, GL_INVALID_OPERATION,
594                  "glGetQueryObjectivARB(id=%d is invalid or active)", id);
595      return;
596   }
597
598   switch (pname) {
599      case GL_QUERY_RESULT_ARB:
600         if (!q->Ready)
601            ctx->Driver.WaitQuery(ctx, q);
602         /* if result is too large for returned type, clamp to max value */
603         if (q->Target == GL_ANY_SAMPLES_PASSED
604             || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
605            if (q->Result)
606               *params = GL_TRUE;
607            else
608               *params = GL_FALSE;
609         } else {
610            if (q->Result > 0x7fffffff) {
611               *params = 0x7fffffff;
612            }
613            else {
614               *params = (GLint)q->Result;
615            }
616         }
617         break;
618      case GL_QUERY_RESULT_AVAILABLE_ARB:
619	 if (!q->Ready)
620	    ctx->Driver.CheckQuery( ctx, q );
621         *params = q->Ready;
622         break;
623      default:
624         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
625         return;
626   }
627}
628
629
630void GLAPIENTRY
631_mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
632{
633   struct gl_query_object *q = NULL;
634   GET_CURRENT_CONTEXT(ctx);
635
636   if (MESA_VERBOSE & VERBOSE_API)
637      _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id,
638                  _mesa_lookup_enum_by_nr(pname));
639
640   if (id)
641      q = _mesa_lookup_query_object(ctx, id);
642
643   if (!q || q->Active) {
644      _mesa_error(ctx, GL_INVALID_OPERATION,
645                  "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
646      return;
647   }
648
649   switch (pname) {
650      case GL_QUERY_RESULT_ARB:
651         if (!q->Ready)
652            ctx->Driver.WaitQuery(ctx, q);
653         /* if result is too large for returned type, clamp to max value */
654         if (q->Target == GL_ANY_SAMPLES_PASSED
655             || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
656            if (q->Result)
657               *params = GL_TRUE;
658            else
659               *params = GL_FALSE;
660         } else {
661            if (q->Result > 0xffffffff) {
662               *params = 0xffffffff;
663            }
664            else {
665               *params = (GLuint)q->Result;
666            }
667         }
668         break;
669      case GL_QUERY_RESULT_AVAILABLE_ARB:
670	 if (!q->Ready)
671	    ctx->Driver.CheckQuery( ctx, q );
672         *params = q->Ready;
673         break;
674      default:
675         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
676         return;
677   }
678}
679
680
681/**
682 * New with GL_EXT_timer_query
683 */
684void GLAPIENTRY
685_mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
686{
687   struct gl_query_object *q = NULL;
688   GET_CURRENT_CONTEXT(ctx);
689
690   if (MESA_VERBOSE & VERBOSE_API)
691      _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id,
692                  _mesa_lookup_enum_by_nr(pname));
693
694   if (id)
695      q = _mesa_lookup_query_object(ctx, id);
696
697   if (!q || q->Active) {
698      _mesa_error(ctx, GL_INVALID_OPERATION,
699                  "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
700      return;
701   }
702
703   switch (pname) {
704      case GL_QUERY_RESULT_ARB:
705         if (!q->Ready)
706            ctx->Driver.WaitQuery(ctx, q);
707         *params = q->Result;
708         break;
709      case GL_QUERY_RESULT_AVAILABLE_ARB:
710	 if (!q->Ready)
711	    ctx->Driver.CheckQuery( ctx, q );
712         *params = q->Ready;
713         break;
714      default:
715         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
716         return;
717   }
718}
719
720
721/**
722 * New with GL_EXT_timer_query
723 */
724void GLAPIENTRY
725_mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
726{
727   struct gl_query_object *q = NULL;
728   GET_CURRENT_CONTEXT(ctx);
729
730   if (MESA_VERBOSE & VERBOSE_API)
731      _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id,
732                  _mesa_lookup_enum_by_nr(pname));
733
734   if (id)
735      q = _mesa_lookup_query_object(ctx, id);
736
737   if (!q || q->Active) {
738      _mesa_error(ctx, GL_INVALID_OPERATION,
739                  "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
740      return;
741   }
742
743   switch (pname) {
744      case GL_QUERY_RESULT_ARB:
745         if (!q->Ready)
746            ctx->Driver.WaitQuery(ctx, q);
747         *params = q->Result;
748         break;
749      case GL_QUERY_RESULT_AVAILABLE_ARB:
750	 if (!q->Ready)
751	    ctx->Driver.CheckQuery( ctx, q );
752         *params = q->Ready;
753         break;
754      default:
755         _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
756         return;
757   }
758}
759
760/**
761 * Allocate/init the context state related to query objects.
762 */
763void
764_mesa_init_queryobj(struct gl_context *ctx)
765{
766   ctx->Query.QueryObjects = _mesa_NewHashTable();
767   ctx->Query.CurrentOcclusionObject = NULL;
768
769   ctx->Const.QueryCounterBits.SamplesPassed = 64;
770   ctx->Const.QueryCounterBits.TimeElapsed = 64;
771   ctx->Const.QueryCounterBits.Timestamp = 64;
772   ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
773   ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
774}
775
776
777/**
778 * Callback for deleting a query object.  Called by _mesa_HashDeleteAll().
779 */
780static void
781delete_queryobj_cb(GLuint id, void *data, void *userData)
782{
783   struct gl_query_object *q= (struct gl_query_object *) data;
784   struct gl_context *ctx = (struct gl_context *)userData;
785   ctx->Driver.DeleteQuery(ctx, q);
786}
787
788
789/**
790 * Free the context state related to query objects.
791 */
792void
793_mesa_free_queryobj_data(struct gl_context *ctx)
794{
795   _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
796   _mesa_DeleteHashTable(ctx->Query.QueryObjects);
797}
798