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 #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 *
_mesa_new_query_object(struct gl_context * ctx,GLuint id)45 _mesa_new_query_object(struct gl_context *ctx, GLuint id)
46 {
47 struct gl_query_object *q = CALLOC_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 /* OpenGL 3.1 § 2.13 says about GenQueries, "These names are marked as
61 * used, but no object is associated with them until the first time they
62 * are used by BeginQuery." Since our implementation actually does
63 * allocate an object at this point, use a flag to indicate that this
64 * object has not yet been bound so should not be considered a query.
65 */
66 q->EverBound = GL_FALSE;
67 }
68 return q;
69 }
70
71
72 /**
73 * Begin a query. Software driver fallback.
74 * Called via ctx->Driver.BeginQuery().
75 */
76 static void
_mesa_begin_query(struct gl_context * ctx,struct gl_query_object * q)77 _mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
78 {
79 ctx->NewState |= _NEW_DEPTH; /* for swrast */
80 }
81
82
83 /**
84 * End a query. Software driver fallback.
85 * Called via ctx->Driver.EndQuery().
86 */
87 static void
_mesa_end_query(struct gl_context * ctx,struct gl_query_object * q)88 _mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
89 {
90 ctx->NewState |= _NEW_DEPTH; /* for swrast */
91 q->Ready = GL_TRUE;
92 }
93
94
95 /**
96 * Wait for query to complete. Software driver fallback.
97 * Called via ctx->Driver.WaitQuery().
98 */
99 static void
_mesa_wait_query(struct gl_context * ctx,struct gl_query_object * q)100 _mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
101 {
102 /* For software drivers, _mesa_end_query() should have completed the query.
103 * For real hardware, implement a proper WaitQuery() driver function,
104 * which may require issuing a flush.
105 */
106 assert(q->Ready);
107 }
108
109
110 /**
111 * Check if a query results are ready. Software driver fallback.
112 * Called via ctx->Driver.CheckQuery().
113 */
114 static void
_mesa_check_query(struct gl_context * ctx,struct gl_query_object * q)115 _mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
116 {
117 /* No-op for sw rendering.
118 * HW drivers may need to flush at this time.
119 */
120 }
121
122
123 /**
124 * Delete a query object. Called via ctx->Driver.DeleteQuery().
125 * Not removed from hash table here.
126 */
127 static void
_mesa_delete_query(struct gl_context * ctx,struct gl_query_object * q)128 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
129 {
130 free(q->Label);
131 free(q);
132 }
133
134
135 void
_mesa_init_query_object_functions(struct dd_function_table * driver)136 _mesa_init_query_object_functions(struct dd_function_table *driver)
137 {
138 driver->NewQueryObject = _mesa_new_query_object;
139 driver->DeleteQuery = _mesa_delete_query;
140 driver->BeginQuery = _mesa_begin_query;
141 driver->EndQuery = _mesa_end_query;
142 driver->WaitQuery = _mesa_wait_query;
143 driver->CheckQuery = _mesa_check_query;
144 }
145
146 static struct gl_query_object **
get_pipe_stats_binding_point(struct gl_context * ctx,GLenum target)147 get_pipe_stats_binding_point(struct gl_context *ctx,
148 GLenum target)
149 {
150 const int which = target - GL_VERTICES_SUBMITTED_ARB;
151 assert(which < MAX_PIPELINE_STATISTICS);
152
153 if (!_mesa_is_desktop_gl(ctx) ||
154 !ctx->Extensions.ARB_pipeline_statistics_query)
155 return NULL;
156
157 return &ctx->Query.pipeline_stats[which];
158 }
159
160 /**
161 * Return pointer to the query object binding point for the given target and
162 * index.
163 * \return NULL if invalid target, else the address of binding point
164 */
165 static struct gl_query_object **
get_query_binding_point(struct gl_context * ctx,GLenum target,GLuint index)166 get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index)
167 {
168
169 /* From GL_EXT_occlusion_query_boolean spec:
170 *
171 * "Accepted by the <target> parameter of BeginQueryEXT, EndQueryEXT,
172 * and GetQueryivEXT:
173 *
174 * ANY_SAMPLES_PASSED_EXT 0x8C2F
175 * ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A"
176 */
177 if ((_mesa_is_gles(ctx) && ctx->Version == 20) &&
178 (target != GL_ANY_SAMPLES_PASSED &&
179 target != GL_ANY_SAMPLES_PASSED_CONSERVATIVE))
180 return NULL;
181
182 switch (target) {
183 case GL_SAMPLES_PASSED_ARB:
184 if (ctx->Extensions.ARB_occlusion_query)
185 return &ctx->Query.CurrentOcclusionObject;
186 else
187 return NULL;
188 case GL_ANY_SAMPLES_PASSED:
189 if (ctx->Extensions.ARB_occlusion_query2)
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_EXT:
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_ARB:
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_ARB:
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_ARB:
226 case GL_PRIMITIVES_SUBMITTED_ARB:
227 case GL_VERTEX_SHADER_INVOCATIONS_ARB:
228 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
229 case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
230 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
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_ARB + MAX_PIPELINE_STATISTICS - 1;
236 /* fallthrough */
237 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB:
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_ARB:
244 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
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_ARB:
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
create_queries(struct gl_context * ctx,GLenum target,GLsizei n,GLuint * ids,bool dsa)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
_mesa_GenQueries(GLsizei n,GLuint * ids)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
_mesa_CreateQueries(GLenum target,GLsizei n,GLuint * ids)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_ARB:
321 case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
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
_mesa_DeleteQueries(GLsizei n,const GLuint * ids)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
_mesa_IsQuery(GLuint id)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
query_error_check_index(struct gl_context * ctx,GLenum target,GLuint index)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_ARB:
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
_mesa_BeginQueryIndexed(GLenum target,GLuint index,GLuint id)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
_mesa_EndQueryIndexed(GLenum target,GLuint index)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
_mesa_BeginQuery(GLenum target,GLuint id)565 _mesa_BeginQuery(GLenum target, GLuint id)
566 {
567 _mesa_BeginQueryIndexed(target, 0, id);
568 }
569
570 void GLAPIENTRY
_mesa_EndQuery(GLenum target)571 _mesa_EndQuery(GLenum target)
572 {
573 _mesa_EndQueryIndexed(target, 0);
574 }
575
576 void GLAPIENTRY
_mesa_QueryCounter(GLuint id,GLenum target)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
_mesa_GetQueryIndexediv(GLenum target,GLuint index,GLenum pname,GLint * params)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_ARB:
697 switch (target) {
698 case GL_SAMPLES_PASSED:
699 *params = ctx->Const.QueryCounterBits.SamplesPassed;
700 break;
701 case GL_ANY_SAMPLES_PASSED:
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_ARB:
721 case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB:
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_ARB:
729 *params = ctx->Const.QueryCounterBits.VerticesSubmitted;
730 break;
731 case GL_PRIMITIVES_SUBMITTED_ARB:
732 *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted;
733 break;
734 case GL_VERTEX_SHADER_INVOCATIONS_ARB:
735 *params = ctx->Const.QueryCounterBits.VsInvocations;
736 break;
737 case GL_TESS_CONTROL_SHADER_PATCHES_ARB:
738 *params = ctx->Const.QueryCounterBits.TessPatches;
739 break;
740 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB:
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_ARB:
747 *params = ctx->Const.QueryCounterBits.GsPrimitives;
748 break;
749 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB:
750 *params = ctx->Const.QueryCounterBits.FsInvocations;
751 break;
752 case GL_COMPUTE_SHADER_INVOCATIONS_ARB:
753 *params = ctx->Const.QueryCounterBits.ComputeInvocations;
754 break;
755 case GL_CLIPPING_INPUT_PRIMITIVES_ARB:
756 *params = ctx->Const.QueryCounterBits.ClInPrimitives;
757 break;
758 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB:
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_ARB:
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
_mesa_GetQueryiv(GLenum target,GLenum pname,GLint * params)779 _mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params)
780 {
781 _mesa_GetQueryIndexediv(target, 0, pname, params);
782 }
783
784 static void
get_query_object(struct gl_context * ctx,const char * func,GLuint id,GLenum pname,GLenum ptype,struct gl_buffer_object * buf,intptr_t offset)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 !ctx->Extensions.EXT_disjoint_timer_query) {
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
_mesa_GetQueryObjectiv(GLuint id,GLenum pname,GLint * params)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
_mesa_GetQueryObjectuiv(GLuint id,GLenum pname,GLuint * params)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
_mesa_GetQueryObjecti64v(GLuint id,GLenum pname,GLint64EXT * params)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
_mesa_GetQueryObjectui64v(GLuint id,GLenum pname,GLuint64EXT * params)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
_mesa_GetQueryBufferObjectiv(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)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
_mesa_GetQueryBufferObjectuiv(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)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
_mesa_GetQueryBufferObjecti64v(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)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
_mesa_GetQueryBufferObjectui64v(GLuint id,GLuint buffer,GLenum pname,GLintptr offset)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
_mesa_init_queryobj(struct gl_context * ctx)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
delete_queryobj_cb(GLuint id,void * data,void * userData)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
_mesa_free_queryobj_data(struct gl_context * ctx)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 }
1074