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