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