2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 #include "mfeatures.h"
34 #include "main/dispatch.h"
41 * Allocate a new query object. This is a fallback routine called via
42 * ctx->Driver.NewQueryObject().
43 * \param ctx - rendering context
44 * \param id - the new object's ID
45 * \return pointer to new query_object object or NULL if out of memory.
47 static struct gl_query_object *
48 _mesa_new_query_object(struct gl_context *ctx, GLuint id)
50 struct gl_query_object *q = MALLOC_STRUCT(gl_query_object);
56 q->Ready = GL_TRUE; /* correct, see spec */
63 * Begin a query. Software driver fallback.
64 * Called via ctx->Driver.BeginQuery().
67 _mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
74 * End a query. Software driver fallback.
75 * Called via ctx->Driver.EndQuery().
78 _mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
85 * Wait for query to complete. Software driver fallback.
86 * Called via ctx->Driver.WaitQuery().
89 _mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
91 /* For software drivers, _mesa_end_query() should have completed the query.
92 * For real hardware, implement a proper WaitQuery() driver function,
93 * which may require issuing a flush.
100 * Check if a query results are ready. Software driver fallback.
101 * Called via ctx->Driver.CheckQuery().
104 _mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
106 /* No-op for sw rendering.
107 * HW drivers may need to flush at this time.
113 * Delete a query object. Called via ctx->Driver.DeleteQuery().
114 * Not removed from hash table here.
117 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
124 _mesa_init_query_object_functions(struct dd_function_table *driver)
126 driver->NewQueryObject = _mesa_new_query_object;
127 driver->DeleteQuery = _mesa_delete_query;
128 driver->BeginQuery = _mesa_begin_query;
129 driver->EndQuery = _mesa_end_query;
130 driver->WaitQuery = _mesa_wait_query;
131 driver->CheckQuery = _mesa_check_query;
136 * Return pointer to the query object binding point for the given target.
137 * \return NULL if invalid target, else the address of binding point
139 static struct gl_query_object **
140 get_query_binding_point(struct gl_context *ctx, GLenum target)
143 case GL_SAMPLES_PASSED_ARB:
144 if (ctx->Extensions.ARB_occlusion_query)
145 return &ctx->Query.CurrentOcclusionObject;
148 case GL_ANY_SAMPLES_PASSED:
149 if (ctx->Extensions.ARB_occlusion_query2)
150 return &ctx->Query.CurrentOcclusionObject;
153 case GL_TIME_ELAPSED_EXT:
154 if (ctx->Extensions.EXT_timer_query)
155 return &ctx->Query.CurrentTimerObject;
158 #if FEATURE_EXT_transform_feedback
159 case GL_PRIMITIVES_GENERATED:
160 if (ctx->Extensions.EXT_transform_feedback)
161 return &ctx->Query.PrimitivesGenerated;
164 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
165 if (ctx->Extensions.EXT_transform_feedback)
166 return &ctx->Query.PrimitivesWritten;
176 static void GLAPIENTRY
177 _mesa_GenQueriesARB(GLsizei n, GLuint *ids)
180 GET_CURRENT_CONTEXT(ctx);
181 ASSERT_OUTSIDE_BEGIN_END(ctx);
183 if (MESA_VERBOSE & VERBOSE_API)
184 _mesa_debug(ctx, "glGenQueries(%d)\n", n);
187 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
191 /* No query objects can be active at this time! */
192 if (ctx->Query.CurrentOcclusionObject ||
193 ctx->Query.CurrentTimerObject) {
194 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
198 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
201 for (i = 0; i < n; i++) {
202 struct gl_query_object *q
203 = ctx->Driver.NewQueryObject(ctx, first + i);
205 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
209 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
215 static void GLAPIENTRY
216 _mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
219 GET_CURRENT_CONTEXT(ctx);
220 ASSERT_OUTSIDE_BEGIN_END(ctx);
221 FLUSH_VERTICES(ctx, 0);
223 if (MESA_VERBOSE & VERBOSE_API)
224 _mesa_debug(ctx, "glDeleeteQueries(%d)\n", n);
227 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
231 /* No query objects can be active at this time! */
232 if (ctx->Query.CurrentOcclusionObject ||
233 ctx->Query.CurrentTimerObject) {
234 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
238 for (i = 0; i < n; i++) {
240 struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
242 ASSERT(!q->Active); /* should be caught earlier */
243 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
244 ctx->Driver.DeleteQuery(ctx, q);
251 static GLboolean GLAPIENTRY
252 _mesa_IsQueryARB(GLuint id)
254 GET_CURRENT_CONTEXT(ctx);
255 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
257 if (MESA_VERBOSE & VERBOSE_API)
258 _mesa_debug(ctx, "glIsQuery(%u)\n", id);
260 if (id && _mesa_lookup_query_object(ctx, id))
267 static void GLAPIENTRY
268 _mesa_BeginQueryARB(GLenum target, GLuint id)
270 struct gl_query_object *q, **bindpt;
271 GET_CURRENT_CONTEXT(ctx);
272 ASSERT_OUTSIDE_BEGIN_END(ctx);
274 if (MESA_VERBOSE & VERBOSE_API)
275 _mesa_debug(ctx, "glBeginQuery(%s, %u)\n",
276 _mesa_lookup_enum_by_nr(target), id);
278 FLUSH_VERTICES(ctx, _NEW_DEPTH);
280 bindpt = get_query_binding_point(ctx, target);
282 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
287 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
291 q = _mesa_lookup_query_object(ctx, id);
293 /* create new object */
294 q = ctx->Driver.NewQueryObject(ctx, id);
296 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
299 _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
302 /* pre-existing object */
304 _mesa_error(ctx, GL_INVALID_OPERATION,
305 "glBeginQueryARB(query already active)");
315 /* XXX should probably refcount query objects */
318 ctx->Driver.BeginQuery(ctx, q);
322 static void GLAPIENTRY
323 _mesa_EndQueryARB(GLenum target)
325 struct gl_query_object *q, **bindpt;
326 GET_CURRENT_CONTEXT(ctx);
327 ASSERT_OUTSIDE_BEGIN_END(ctx);
329 if (MESA_VERBOSE & VERBOSE_API)
330 _mesa_debug(ctx, "glEndQuery(%s)\n", _mesa_lookup_enum_by_nr(target));
332 FLUSH_VERTICES(ctx, _NEW_DEPTH);
334 bindpt = get_query_binding_point(ctx, target);
336 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
340 /* XXX should probably refcount query objects */
344 if (!q || !q->Active) {
345 _mesa_error(ctx, GL_INVALID_OPERATION,
346 "glEndQueryARB(no matching glBeginQueryARB)");
350 q->Active = GL_FALSE;
351 ctx->Driver.EndQuery(ctx, q);
355 static void GLAPIENTRY
356 _mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
358 struct gl_query_object *q, **bindpt;
359 GET_CURRENT_CONTEXT(ctx);
360 ASSERT_OUTSIDE_BEGIN_END(ctx);
362 if (MESA_VERBOSE & VERBOSE_API)
363 _mesa_debug(ctx, "glGetQueryiv(%s, %s)\n",
364 _mesa_lookup_enum_by_nr(target),
365 _mesa_lookup_enum_by_nr(pname));
367 bindpt = get_query_binding_point(ctx, target);
369 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
376 case GL_QUERY_COUNTER_BITS_ARB:
377 *params = 8 * sizeof(q->Result);
379 case GL_CURRENT_QUERY_ARB:
380 *params = q ? q->Id : 0;
383 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
389 static void GLAPIENTRY
390 _mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
392 struct gl_query_object *q = NULL;
393 GET_CURRENT_CONTEXT(ctx);
394 ASSERT_OUTSIDE_BEGIN_END(ctx);
396 if (MESA_VERBOSE & VERBOSE_API)
397 _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id,
398 _mesa_lookup_enum_by_nr(pname));
401 q = _mesa_lookup_query_object(ctx, id);
403 if (!q || q->Active) {
404 _mesa_error(ctx, GL_INVALID_OPERATION,
405 "glGetQueryObjectivARB(id=%d is invalid or active)", id);
410 case GL_QUERY_RESULT_ARB:
412 ctx->Driver.WaitQuery(ctx, q);
413 /* if result is too large for returned type, clamp to max value */
414 if (q->Target == GL_ANY_SAMPLES_PASSED) {
420 if (q->Result > 0x7fffffff) {
421 *params = 0x7fffffff;
424 *params = (GLint)q->Result;
428 case GL_QUERY_RESULT_AVAILABLE_ARB:
430 ctx->Driver.CheckQuery( ctx, q );
434 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
440 static void GLAPIENTRY
441 _mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
443 struct gl_query_object *q = NULL;
444 GET_CURRENT_CONTEXT(ctx);
445 ASSERT_OUTSIDE_BEGIN_END(ctx);
447 if (MESA_VERBOSE & VERBOSE_API)
448 _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id,
449 _mesa_lookup_enum_by_nr(pname));
452 q = _mesa_lookup_query_object(ctx, id);
454 if (!q || q->Active) {
455 _mesa_error(ctx, GL_INVALID_OPERATION,
456 "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
461 case GL_QUERY_RESULT_ARB:
463 ctx->Driver.WaitQuery(ctx, q);
464 /* if result is too large for returned type, clamp to max value */
465 if (q->Target == GL_ANY_SAMPLES_PASSED) {
471 if (q->Result > 0xffffffff) {
472 *params = 0xffffffff;
475 *params = (GLuint)q->Result;
479 case GL_QUERY_RESULT_AVAILABLE_ARB:
481 ctx->Driver.CheckQuery( ctx, q );
485 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
492 * New with GL_EXT_timer_query
494 static void GLAPIENTRY
495 _mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
497 struct gl_query_object *q = NULL;
498 GET_CURRENT_CONTEXT(ctx);
499 ASSERT_OUTSIDE_BEGIN_END(ctx);
501 if (MESA_VERBOSE & VERBOSE_API)
502 _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id,
503 _mesa_lookup_enum_by_nr(pname));
506 q = _mesa_lookup_query_object(ctx, id);
508 if (!q || q->Active) {
509 _mesa_error(ctx, GL_INVALID_OPERATION,
510 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
515 case GL_QUERY_RESULT_ARB:
517 ctx->Driver.WaitQuery(ctx, q);
520 case GL_QUERY_RESULT_AVAILABLE_ARB:
522 ctx->Driver.CheckQuery( ctx, q );
526 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
533 * New with GL_EXT_timer_query
535 static void GLAPIENTRY
536 _mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
538 struct gl_query_object *q = NULL;
539 GET_CURRENT_CONTEXT(ctx);
540 ASSERT_OUTSIDE_BEGIN_END(ctx);
542 if (MESA_VERBOSE & VERBOSE_API)
543 _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id,
544 _mesa_lookup_enum_by_nr(pname));
547 q = _mesa_lookup_query_object(ctx, id);
549 if (!q || q->Active) {
550 _mesa_error(ctx, GL_INVALID_OPERATION,
551 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
556 case GL_QUERY_RESULT_ARB:
558 ctx->Driver.WaitQuery(ctx, q);
561 case GL_QUERY_RESULT_AVAILABLE_ARB:
563 ctx->Driver.CheckQuery( ctx, q );
567 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
574 _mesa_init_queryobj_dispatch(struct _glapi_table *disp)
576 SET_GenQueriesARB(disp, _mesa_GenQueriesARB);
577 SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB);
578 SET_IsQueryARB(disp, _mesa_IsQueryARB);
579 SET_BeginQueryARB(disp, _mesa_BeginQueryARB);
580 SET_EndQueryARB(disp, _mesa_EndQueryARB);
581 SET_GetQueryivARB(disp, _mesa_GetQueryivARB);
582 SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB);
583 SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB);
585 SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT);
586 SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT);
590 #endif /* FEATURE_queryobj */
594 * Allocate/init the context state related to query objects.
597 _mesa_init_queryobj(struct gl_context *ctx)
599 ctx->Query.QueryObjects = _mesa_NewHashTable();
600 ctx->Query.CurrentOcclusionObject = NULL;
605 * Callback for deleting a query object. Called by _mesa_HashDeleteAll().
608 delete_queryobj_cb(GLuint id, void *data, void *userData)
610 struct gl_query_object *q= (struct gl_query_object *) data;
611 struct gl_context *ctx = (struct gl_context *)userData;
612 ctx->Driver.DeleteQuery(ctx, q);
617 * Free the context state related to query objects.
620 _mesa_free_queryobj_data(struct gl_context *ctx)
622 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
623 _mesa_DeleteHashTable(ctx->Query.QueryObjects);