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