Update comment to specify actual text being referenced from the specification.
[mesa.git] / src / mesa / main / queryobj.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 7.1
4 *
5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR 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 "mfeatures.h"
33 #include "mtypes.h"
34 #include "main/dispatch.h"
35
36
37 /**
38 * Allocate a new query object. This is a fallback routine called via
39 * ctx->Driver.NewQueryObject().
40 * \param ctx - rendering context
41 * \param id - the new object's ID
42 * \return pointer to new query_object object or NULL if out of memory.
43 */
44 static struct gl_query_object *
45 _mesa_new_query_object(struct gl_context *ctx, GLuint id)
46 {
47 struct gl_query_object *q = MALLOC_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 return q;
61 }
62
63
64 /**
65 * Begin a query. Software driver fallback.
66 * Called via ctx->Driver.BeginQuery().
67 */
68 static void
69 _mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
70 {
71 /* no-op */
72 }
73
74
75 /**
76 * End a query. Software driver fallback.
77 * Called via ctx->Driver.EndQuery().
78 */
79 static void
80 _mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
81 {
82 q->Ready = GL_TRUE;
83 }
84
85
86 /**
87 * Wait for query to complete. Software driver fallback.
88 * Called via ctx->Driver.WaitQuery().
89 */
90 static void
91 _mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
92 {
93 /* For software drivers, _mesa_end_query() should have completed the query.
94 * For real hardware, implement a proper WaitQuery() driver function,
95 * which may require issuing a flush.
96 */
97 assert(q->Ready);
98 }
99
100
101 /**
102 * Check if a query results are ready. Software driver fallback.
103 * Called via ctx->Driver.CheckQuery().
104 */
105 static void
106 _mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
107 {
108 /* No-op for sw rendering.
109 * HW drivers may need to flush at this time.
110 */
111 }
112
113
114 /**
115 * Delete a query object. Called via ctx->Driver.DeleteQuery().
116 * Not removed from hash table here.
117 */
118 static void
119 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
120 {
121 free(q);
122 }
123
124
125 void
126 _mesa_init_query_object_functions(struct dd_function_table *driver)
127 {
128 driver->NewQueryObject = _mesa_new_query_object;
129 driver->DeleteQuery = _mesa_delete_query;
130 driver->BeginQuery = _mesa_begin_query;
131 driver->EndQuery = _mesa_end_query;
132 driver->WaitQuery = _mesa_wait_query;
133 driver->CheckQuery = _mesa_check_query;
134 }
135
136
137 /**
138 * Return pointer to the query object binding point for the given target.
139 * \return NULL if invalid target, else the address of binding point
140 */
141 static struct gl_query_object **
142 get_query_binding_point(struct gl_context *ctx, GLenum target)
143 {
144 switch (target) {
145 case GL_SAMPLES_PASSED_ARB:
146 if (ctx->Extensions.ARB_occlusion_query)
147 return &ctx->Query.CurrentOcclusionObject;
148 else
149 return NULL;
150 case GL_ANY_SAMPLES_PASSED:
151 if (ctx->Extensions.ARB_occlusion_query2)
152 return &ctx->Query.CurrentOcclusionObject;
153 else
154 return NULL;
155 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
156 if (ctx->Extensions.ARB_ES3_compatibility
157 || (ctx->API == API_OPENGLES2 && ctx->Version >= 30))
158 return &ctx->Query.CurrentOcclusionObject;
159 else
160 return NULL;
161 case GL_TIME_ELAPSED_EXT:
162 if (ctx->Extensions.EXT_timer_query)
163 return &ctx->Query.CurrentTimerObject;
164 else
165 return NULL;
166 case GL_PRIMITIVES_GENERATED:
167 if (ctx->Extensions.EXT_transform_feedback)
168 return &ctx->Query.PrimitivesGenerated;
169 else
170 return NULL;
171 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
172 if (ctx->Extensions.EXT_transform_feedback)
173 return &ctx->Query.PrimitivesWritten;
174 else
175 return NULL;
176 default:
177 return NULL;
178 }
179 }
180
181
182 void GLAPIENTRY
183 _mesa_GenQueries(GLsizei n, GLuint *ids)
184 {
185 GLuint first;
186 GET_CURRENT_CONTEXT(ctx);
187 ASSERT_OUTSIDE_BEGIN_END(ctx);
188
189 if (MESA_VERBOSE & VERBOSE_API)
190 _mesa_debug(ctx, "glGenQueries(%d)\n", n);
191
192 if (n < 0) {
193 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
194 return;
195 }
196
197 /* No query objects can be active at this time! */
198 if (ctx->Query.CurrentOcclusionObject ||
199 ctx->Query.CurrentTimerObject) {
200 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
201 return;
202 }
203
204 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
205 if (first) {
206 GLsizei i;
207 for (i = 0; i < n; i++) {
208 struct gl_query_object *q
209 = ctx->Driver.NewQueryObject(ctx, first + i);
210 if (!q) {
211 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
212 return;
213 }
214 ids[i] = first + i;
215 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
216 }
217 }
218 }
219
220
221 void GLAPIENTRY
222 _mesa_DeleteQueries(GLsizei n, const GLuint *ids)
223 {
224 GLint i;
225 GET_CURRENT_CONTEXT(ctx);
226 ASSERT_OUTSIDE_BEGIN_END(ctx);
227 FLUSH_VERTICES(ctx, 0);
228
229 if (MESA_VERBOSE & VERBOSE_API)
230 _mesa_debug(ctx, "glDeleteQueries(%d)\n", n);
231
232 if (n < 0) {
233 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
234 return;
235 }
236
237 /* No query objects can be active at this time! */
238 if (ctx->Query.CurrentOcclusionObject ||
239 ctx->Query.CurrentTimerObject) {
240 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
241 return;
242 }
243
244 for (i = 0; i < n; i++) {
245 if (ids[i] > 0) {
246 struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
247 if (q) {
248 ASSERT(!q->Active); /* should be caught earlier */
249 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
250 ctx->Driver.DeleteQuery(ctx, q);
251 }
252 }
253 }
254 }
255
256
257 GLboolean GLAPIENTRY
258 _mesa_IsQuery(GLuint id)
259 {
260 GET_CURRENT_CONTEXT(ctx);
261 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
262
263 if (MESA_VERBOSE & VERBOSE_API)
264 _mesa_debug(ctx, "glIsQuery(%u)\n", id);
265
266 if (id && _mesa_lookup_query_object(ctx, id))
267 return GL_TRUE;
268 else
269 return GL_FALSE;
270 }
271
272 static GLboolean
273 query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index)
274 {
275 switch (target) {
276 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
277 case GL_PRIMITIVES_GENERATED:
278 if (index >= ctx->Const.MaxVertexStreams) {
279 _mesa_error(ctx, GL_INVALID_VALUE,
280 "glBeginQueryIndexed(index>=MaxVertexStreams)");
281 return GL_FALSE;
282 }
283 break;
284 default:
285 if (index > 0) {
286 _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)");
287 return GL_FALSE;
288 }
289 }
290 return GL_TRUE;
291 }
292
293 void GLAPIENTRY
294 _mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id)
295 {
296 struct gl_query_object *q, **bindpt;
297 GET_CURRENT_CONTEXT(ctx);
298 ASSERT_OUTSIDE_BEGIN_END(ctx);
299
300 if (MESA_VERBOSE & VERBOSE_API)
301 _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n",
302 _mesa_lookup_enum_by_nr(target), index, id);
303
304 if (!query_error_check_index(ctx, target, index))
305 return;
306
307 FLUSH_VERTICES(ctx, _NEW_DEPTH);
308
309 bindpt = get_query_binding_point(ctx, target);
310 if (!bindpt) {
311 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)");
312 return;
313 }
314
315 /* From the GL_ARB_occlusion_query spec:
316 *
317 * "If BeginQueryARB is called while another query is already in
318 * progress with the same target, an INVALID_OPERATION error is
319 * generated."
320 */
321 if (*bindpt) {
322 _mesa_error(ctx, GL_INVALID_OPERATION,
323 "glBeginQuery{Indexed}(target=%s is active)",
324 _mesa_lookup_enum_by_nr(target));
325 return;
326 }
327
328 if (id == 0) {
329 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)");
330 return;
331 }
332
333 q = _mesa_lookup_query_object(ctx, id);
334 if (!q) {
335 if (ctx->API != API_OPENGL_COMPAT) {
336 _mesa_error(ctx, GL_INVALID_OPERATION,
337 "glBeginQuery{Indexed}(non-gen name)");
338 return;
339 } else {
340 /* create new object */
341 q = ctx->Driver.NewQueryObject(ctx, id);
342 if (!q) {
343 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}");
344 return;
345 }
346 _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
347 }
348 }
349 else {
350 /* pre-existing object */
351 if (q->Active) {
352 _mesa_error(ctx, GL_INVALID_OPERATION,
353 "glBeginQuery{Indexed}(query already active)");
354 return;
355 }
356 }
357
358 q->Target = target;
359 q->Active = GL_TRUE;
360 q->Result = 0;
361 q->Ready = GL_FALSE;
362
363 /* XXX should probably refcount query objects */
364 *bindpt = q;
365
366 ctx->Driver.BeginQuery(ctx, q);
367 }
368
369
370 void GLAPIENTRY
371 _mesa_EndQueryIndexed(GLenum target, GLuint index)
372 {
373 struct gl_query_object *q, **bindpt;
374 GET_CURRENT_CONTEXT(ctx);
375 ASSERT_OUTSIDE_BEGIN_END(ctx);
376
377 if (MESA_VERBOSE & VERBOSE_API)
378 _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n",
379 _mesa_lookup_enum_by_nr(target), index);
380
381 if (!query_error_check_index(ctx, target, index))
382 return;
383
384 FLUSH_VERTICES(ctx, _NEW_DEPTH);
385
386 bindpt = get_query_binding_point(ctx, target);
387 if (!bindpt) {
388 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)");
389 return;
390 }
391
392 /* XXX should probably refcount query objects */
393 q = *bindpt;
394
395 /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */
396 if (q && q->Target != target) {
397 _mesa_error(ctx, GL_INVALID_OPERATION,
398 "glEndQuery(target=%s with active query of target %s)",
399 _mesa_lookup_enum_by_nr(target),
400 _mesa_lookup_enum_by_nr(q->Target));
401 return;
402 }
403
404 *bindpt = NULL;
405
406 if (!q || !q->Active) {
407 _mesa_error(ctx, GL_INVALID_OPERATION,
408 "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})");
409 return;
410 }
411
412 q->Active = GL_FALSE;
413 ctx->Driver.EndQuery(ctx, q);
414 }
415
416 void GLAPIENTRY
417 _mesa_BeginQuery(GLenum target, GLuint id)
418 {
419 _mesa_BeginQueryIndexed(target, 0, id);
420 }
421
422 void GLAPIENTRY
423 _mesa_EndQuery(GLenum target)
424 {
425 _mesa_EndQueryIndexed(target, 0);
426 }
427
428 void GLAPIENTRY
429 _mesa_QueryCounter(GLuint id, GLenum target)
430 {
431 struct gl_query_object *q;
432 GET_CURRENT_CONTEXT(ctx);
433 ASSERT_OUTSIDE_BEGIN_END(ctx);
434
435 if (MESA_VERBOSE & VERBOSE_API)
436 _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id,
437 _mesa_lookup_enum_by_nr(target));
438
439 /* error checking */
440 if (target != GL_TIMESTAMP) {
441 _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)");
442 return;
443 }
444
445 if (id == 0) {
446 _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)");
447 return;
448 }
449
450 q = _mesa_lookup_query_object(ctx, id);
451 if (!q) {
452 /* XXX the Core profile should throw INVALID_OPERATION here */
453
454 /* create new object */
455 q = ctx->Driver.NewQueryObject(ctx, id);
456 if (!q) {
457 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter");
458 return;
459 }
460 _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
461 }
462 else {
463 if (q->Target && q->Target != GL_TIMESTAMP) {
464 _mesa_error(ctx, GL_INVALID_OPERATION,
465 "glQueryCounter(id has an invalid target)");
466 return;
467 }
468 }
469
470 if (q->Active) {
471 _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)");
472 return;
473 }
474
475 q->Target = target;
476 q->Result = 0;
477 q->Ready = GL_FALSE;
478
479 /* QueryCounter is implemented using EndQuery without BeginQuery
480 * in drivers. This is actually Direct3D and Gallium convention. */
481 ctx->Driver.EndQuery(ctx, q);
482 }
483
484
485 void GLAPIENTRY
486 _mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname,
487 GLint *params)
488 {
489 struct gl_query_object *q = NULL, **bindpt = NULL;
490 GET_CURRENT_CONTEXT(ctx);
491 ASSERT_OUTSIDE_BEGIN_END(ctx);
492
493 if (MESA_VERBOSE & VERBOSE_API)
494 _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n",
495 _mesa_lookup_enum_by_nr(target),
496 index,
497 _mesa_lookup_enum_by_nr(pname));
498
499 if (!query_error_check_index(ctx, target, index))
500 return;
501
502 if (target == GL_TIMESTAMP) {
503 if (!ctx->Extensions.ARB_timer_query) {
504 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
505 return;
506 }
507 }
508 else {
509 bindpt = get_query_binding_point(ctx, target);
510 if (!bindpt) {
511 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)");
512 return;
513 }
514
515 q = *bindpt;
516 }
517
518 switch (pname) {
519 case GL_QUERY_COUNTER_BITS_ARB:
520 switch (target) {
521 case GL_SAMPLES_PASSED:
522 *params = ctx->Const.QueryCounterBits.SamplesPassed;
523 break;
524 case GL_ANY_SAMPLES_PASSED:
525 /* The minimum value of this is 1 if it's nonzero, and the value
526 * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more
527 * bits.
528 */
529 *params = 1;
530 break;
531 case GL_TIME_ELAPSED:
532 *params = ctx->Const.QueryCounterBits.TimeElapsed;
533 break;
534 case GL_TIMESTAMP:
535 *params = ctx->Const.QueryCounterBits.Timestamp;
536 break;
537 case GL_PRIMITIVES_GENERATED:
538 *params = ctx->Const.QueryCounterBits.PrimitivesGenerated;
539 break;
540 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
541 *params = ctx->Const.QueryCounterBits.PrimitivesWritten;
542 break;
543 default:
544 _mesa_problem(ctx,
545 "Unknown target in glGetQueryIndexediv(target = %s)",
546 _mesa_lookup_enum_by_nr(target));
547 *params = 0;
548 break;
549 }
550 break;
551 case GL_CURRENT_QUERY_ARB:
552 *params = (q && q->Target == target) ? q->Id : 0;
553 break;
554 default:
555 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)");
556 return;
557 }
558 }
559
560 void GLAPIENTRY
561 _mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
562 {
563 _mesa_GetQueryIndexediv(target, 0, pname, params);
564 }
565
566 void GLAPIENTRY
567 _mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params)
568 {
569 struct gl_query_object *q = NULL;
570 GET_CURRENT_CONTEXT(ctx);
571 ASSERT_OUTSIDE_BEGIN_END(ctx);
572
573 if (MESA_VERBOSE & VERBOSE_API)
574 _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id,
575 _mesa_lookup_enum_by_nr(pname));
576
577 if (id)
578 q = _mesa_lookup_query_object(ctx, id);
579
580 if (!q || q->Active) {
581 _mesa_error(ctx, GL_INVALID_OPERATION,
582 "glGetQueryObjectivARB(id=%d is invalid or active)", id);
583 return;
584 }
585
586 switch (pname) {
587 case GL_QUERY_RESULT_ARB:
588 if (!q->Ready)
589 ctx->Driver.WaitQuery(ctx, q);
590 /* if result is too large for returned type, clamp to max value */
591 if (q->Target == GL_ANY_SAMPLES_PASSED
592 || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
593 if (q->Result)
594 *params = GL_TRUE;
595 else
596 *params = GL_FALSE;
597 } else {
598 if (q->Result > 0x7fffffff) {
599 *params = 0x7fffffff;
600 }
601 else {
602 *params = (GLint)q->Result;
603 }
604 }
605 break;
606 case GL_QUERY_RESULT_AVAILABLE_ARB:
607 if (!q->Ready)
608 ctx->Driver.CheckQuery( ctx, q );
609 *params = q->Ready;
610 break;
611 default:
612 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
613 return;
614 }
615 }
616
617
618 void GLAPIENTRY
619 _mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params)
620 {
621 struct gl_query_object *q = NULL;
622 GET_CURRENT_CONTEXT(ctx);
623 ASSERT_OUTSIDE_BEGIN_END(ctx);
624
625 if (MESA_VERBOSE & VERBOSE_API)
626 _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id,
627 _mesa_lookup_enum_by_nr(pname));
628
629 if (id)
630 q = _mesa_lookup_query_object(ctx, id);
631
632 if (!q || q->Active) {
633 _mesa_error(ctx, GL_INVALID_OPERATION,
634 "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
635 return;
636 }
637
638 switch (pname) {
639 case GL_QUERY_RESULT_ARB:
640 if (!q->Ready)
641 ctx->Driver.WaitQuery(ctx, q);
642 /* if result is too large for returned type, clamp to max value */
643 if (q->Target == GL_ANY_SAMPLES_PASSED
644 || q->Target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
645 if (q->Result)
646 *params = GL_TRUE;
647 else
648 *params = GL_FALSE;
649 } else {
650 if (q->Result > 0xffffffff) {
651 *params = 0xffffffff;
652 }
653 else {
654 *params = (GLuint)q->Result;
655 }
656 }
657 break;
658 case GL_QUERY_RESULT_AVAILABLE_ARB:
659 if (!q->Ready)
660 ctx->Driver.CheckQuery( ctx, q );
661 *params = q->Ready;
662 break;
663 default:
664 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
665 return;
666 }
667 }
668
669
670 /**
671 * New with GL_EXT_timer_query
672 */
673 void GLAPIENTRY
674 _mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params)
675 {
676 struct gl_query_object *q = NULL;
677 GET_CURRENT_CONTEXT(ctx);
678 ASSERT_OUTSIDE_BEGIN_END(ctx);
679
680 if (MESA_VERBOSE & VERBOSE_API)
681 _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id,
682 _mesa_lookup_enum_by_nr(pname));
683
684 if (id)
685 q = _mesa_lookup_query_object(ctx, id);
686
687 if (!q || q->Active) {
688 _mesa_error(ctx, GL_INVALID_OPERATION,
689 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
690 return;
691 }
692
693 switch (pname) {
694 case GL_QUERY_RESULT_ARB:
695 if (!q->Ready)
696 ctx->Driver.WaitQuery(ctx, q);
697 *params = q->Result;
698 break;
699 case GL_QUERY_RESULT_AVAILABLE_ARB:
700 if (!q->Ready)
701 ctx->Driver.CheckQuery( ctx, q );
702 *params = q->Ready;
703 break;
704 default:
705 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
706 return;
707 }
708 }
709
710
711 /**
712 * New with GL_EXT_timer_query
713 */
714 void GLAPIENTRY
715 _mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params)
716 {
717 struct gl_query_object *q = NULL;
718 GET_CURRENT_CONTEXT(ctx);
719 ASSERT_OUTSIDE_BEGIN_END(ctx);
720
721 if (MESA_VERBOSE & VERBOSE_API)
722 _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id,
723 _mesa_lookup_enum_by_nr(pname));
724
725 if (id)
726 q = _mesa_lookup_query_object(ctx, id);
727
728 if (!q || q->Active) {
729 _mesa_error(ctx, GL_INVALID_OPERATION,
730 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
731 return;
732 }
733
734 switch (pname) {
735 case GL_QUERY_RESULT_ARB:
736 if (!q->Ready)
737 ctx->Driver.WaitQuery(ctx, q);
738 *params = q->Result;
739 break;
740 case GL_QUERY_RESULT_AVAILABLE_ARB:
741 if (!q->Ready)
742 ctx->Driver.CheckQuery( ctx, q );
743 *params = q->Ready;
744 break;
745 default:
746 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
747 return;
748 }
749 }
750
751
752 void
753 _mesa_init_queryobj_dispatch(const struct gl_context *ctx,
754 struct _glapi_table *disp)
755 {
756 SET_GenQueries(disp, _mesa_GenQueries);
757 SET_DeleteQueries(disp, _mesa_DeleteQueries);
758 SET_IsQuery(disp, _mesa_IsQuery);
759 SET_BeginQuery(disp, _mesa_BeginQuery);
760 SET_EndQuery(disp, _mesa_EndQuery);
761 SET_GetQueryiv(disp, _mesa_GetQueryiv);
762 SET_GetQueryObjectuiv(disp, _mesa_GetQueryObjectuiv);
763
764 if (_mesa_is_desktop_gl(ctx)) {
765 SET_GetQueryObjectiv(disp, _mesa_GetQueryObjectiv);
766 SET_QueryCounter(disp, _mesa_QueryCounter);
767
768 SET_GetQueryObjecti64v(disp, _mesa_GetQueryObjecti64v);
769 SET_GetQueryObjectui64v(disp, _mesa_GetQueryObjectui64v);
770
771 SET_BeginQueryIndexed(disp, _mesa_BeginQueryIndexed);
772 SET_EndQueryIndexed(disp, _mesa_EndQueryIndexed);
773 SET_GetQueryIndexediv(disp, _mesa_GetQueryIndexediv);
774 }
775 }
776
777
778 /**
779 * Allocate/init the context state related to query objects.
780 */
781 void
782 _mesa_init_queryobj(struct gl_context *ctx)
783 {
784 ctx->Query.QueryObjects = _mesa_NewHashTable();
785 ctx->Query.CurrentOcclusionObject = NULL;
786
787 ctx->Const.QueryCounterBits.SamplesPassed = 64;
788 ctx->Const.QueryCounterBits.TimeElapsed = 64;
789 ctx->Const.QueryCounterBits.Timestamp = 64;
790 ctx->Const.QueryCounterBits.PrimitivesGenerated = 64;
791 ctx->Const.QueryCounterBits.PrimitivesWritten = 64;
792 }
793
794
795 /**
796 * Callback for deleting a query object. Called by _mesa_HashDeleteAll().
797 */
798 static void
799 delete_queryobj_cb(GLuint id, void *data, void *userData)
800 {
801 struct gl_query_object *q= (struct gl_query_object *) data;
802 struct gl_context *ctx = (struct gl_context *)userData;
803 ctx->Driver.DeleteQuery(ctx, q);
804 }
805
806
807 /**
808 * Free the context state related to query objects.
809 */
810 void
811 _mesa_free_queryobj_data(struct gl_context *ctx)
812 {
813 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
814 _mesa_DeleteHashTable(ctx->Query.QueryObjects);
815 }