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