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 "glapi/dispatch.h"
39 * Allocate a new query object. This is a fallback routine called via
40 * ctx->Driver.NewQueryObject().
41 * \param ctx - rendering context
42 * \param id - the new object's ID
43 * \return pointer to new query_object object or NULL if out of memory.
45 static struct gl_query_object
*
46 _mesa_new_query_object(GLcontext
*ctx
, GLuint id
)
48 struct gl_query_object
*q
= MALLOC_STRUCT(gl_query_object
);
54 q
->Ready
= GL_TRUE
; /* correct, see spec */
61 * Begin a query. Software driver fallback.
62 * Called via ctx->Driver.BeginQuery().
65 _mesa_begin_query(GLcontext
*ctx
, struct gl_query_object
*q
)
72 * End a query. Software driver fallback.
73 * Called via ctx->Driver.EndQuery().
76 _mesa_end_query(GLcontext
*ctx
, struct gl_query_object
*q
)
83 * Wait for query to complete. Software driver fallback.
84 * Called via ctx->Driver.WaitQuery().
87 _mesa_wait_query(GLcontext
*ctx
, struct gl_query_object
*q
)
89 /* For software drivers, _mesa_end_query() should have completed the query.
90 * For real hardware, implement a proper WaitQuery() driver function,
91 * which may require issuing a flush.
98 * Check if a query results are ready. Software driver fallback.
99 * Called via ctx->Driver.CheckQuery().
102 _mesa_check_query(GLcontext
*ctx
, struct gl_query_object
*q
)
104 /* No-op for sw rendering.
105 * HW drivers may need to flush at this time.
111 * Delete a query object. Called via ctx->Driver.DeleteQuery().
112 * Not removed from hash table here.
115 _mesa_delete_query(GLcontext
*ctx
, struct gl_query_object
*q
)
121 static struct gl_query_object
*
122 lookup_query_object(GLcontext
*ctx
, GLuint id
)
124 return (struct gl_query_object
*)
125 _mesa_HashLookup(ctx
->Query
.QueryObjects
, id
);
131 _mesa_init_query_object_functions(struct dd_function_table
*driver
)
133 driver
->NewQueryObject
= _mesa_new_query_object
;
134 driver
->DeleteQuery
= _mesa_delete_query
;
135 driver
->BeginQuery
= _mesa_begin_query
;
136 driver
->EndQuery
= _mesa_end_query
;
137 driver
->WaitQuery
= _mesa_wait_query
;
138 driver
->CheckQuery
= _mesa_check_query
;
143 _mesa_GenQueriesARB(GLsizei n
, GLuint
*ids
)
146 GET_CURRENT_CONTEXT(ctx
);
147 ASSERT_OUTSIDE_BEGIN_END(ctx
);
150 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenQueriesARB(n < 0)");
154 /* No query objects can be active at this time! */
155 if (ctx
->Query
.CurrentOcclusionObject
||
156 ctx
->Query
.CurrentTimerObject
) {
157 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glGenQueriesARB");
161 first
= _mesa_HashFindFreeKeyBlock(ctx
->Query
.QueryObjects
, n
);
164 for (i
= 0; i
< n
; i
++) {
165 struct gl_query_object
*q
166 = ctx
->Driver
.NewQueryObject(ctx
, first
+ i
);
168 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenQueriesARB");
172 _mesa_HashInsert(ctx
->Query
.QueryObjects
, first
+ i
, q
);
179 _mesa_DeleteQueriesARB(GLsizei n
, const GLuint
*ids
)
182 GET_CURRENT_CONTEXT(ctx
);
183 ASSERT_OUTSIDE_BEGIN_END(ctx
);
186 _mesa_error(ctx
, GL_INVALID_VALUE
, "glDeleteQueriesARB(n < 0)");
190 /* No query objects can be active at this time! */
191 if (ctx
->Query
.CurrentOcclusionObject
||
192 ctx
->Query
.CurrentTimerObject
) {
193 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glDeleteQueriesARB");
197 for (i
= 0; i
< n
; i
++) {
199 struct gl_query_object
*q
= lookup_query_object(ctx
, ids
[i
]);
201 ASSERT(!q
->Active
); /* should be caught earlier */
202 _mesa_HashRemove(ctx
->Query
.QueryObjects
, ids
[i
]);
203 ctx
->Driver
.DeleteQuery(ctx
, q
);
211 _mesa_IsQueryARB(GLuint id
)
213 GET_CURRENT_CONTEXT(ctx
);
214 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, GL_FALSE
);
216 if (id
&& lookup_query_object(ctx
, id
))
223 static void GLAPIENTRY
224 _mesa_BeginQueryARB(GLenum target
, GLuint id
)
226 struct gl_query_object
*q
;
227 GET_CURRENT_CONTEXT(ctx
);
228 ASSERT_OUTSIDE_BEGIN_END(ctx
);
230 FLUSH_VERTICES(ctx
, _NEW_DEPTH
);
233 case GL_SAMPLES_PASSED_ARB
:
234 if (!ctx
->Extensions
.ARB_occlusion_query
) {
235 _mesa_error(ctx
, GL_INVALID_ENUM
, "glBeginQueryARB(target)");
238 if (ctx
->Query
.CurrentOcclusionObject
) {
239 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glBeginQueryARB");
243 case GL_TIME_ELAPSED_EXT
:
244 if (!ctx
->Extensions
.EXT_timer_query
) {
245 _mesa_error(ctx
, GL_INVALID_ENUM
, "glBeginQueryARB(target)");
248 if (ctx
->Query
.CurrentTimerObject
) {
249 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glBeginQueryARB");
254 _mesa_error(ctx
, GL_INVALID_ENUM
, "glBeginQueryARB(target)");
259 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glBeginQueryARB(id==0)");
263 q
= lookup_query_object(ctx
, id
);
265 /* create new object */
266 q
= ctx
->Driver
.NewQueryObject(ctx
, id
);
268 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glBeginQueryARB");
271 _mesa_HashInsert(ctx
->Query
.QueryObjects
, id
, q
);
274 /* pre-existing object */
276 _mesa_error(ctx
, GL_INVALID_OPERATION
,
277 "glBeginQueryARB(query already active)");
287 if (target
== GL_SAMPLES_PASSED_ARB
) {
288 ctx
->Query
.CurrentOcclusionObject
= q
;
290 else if (target
== GL_TIME_ELAPSED_EXT
) {
291 ctx
->Query
.CurrentTimerObject
= q
;
294 ctx
->Driver
.BeginQuery(ctx
, q
);
298 static void GLAPIENTRY
299 _mesa_EndQueryARB(GLenum target
)
301 struct gl_query_object
*q
;
302 GET_CURRENT_CONTEXT(ctx
);
303 ASSERT_OUTSIDE_BEGIN_END(ctx
);
305 FLUSH_VERTICES(ctx
, _NEW_DEPTH
);
308 case GL_SAMPLES_PASSED_ARB
:
309 if (!ctx
->Extensions
.ARB_occlusion_query
) {
310 _mesa_error(ctx
, GL_INVALID_ENUM
, "glEndQueryARB(target)");
313 q
= ctx
->Query
.CurrentOcclusionObject
;
314 ctx
->Query
.CurrentOcclusionObject
= NULL
;
316 case GL_TIME_ELAPSED_EXT
:
317 if (!ctx
->Extensions
.EXT_timer_query
) {
318 _mesa_error(ctx
, GL_INVALID_ENUM
, "glEndQueryARB(target)");
321 q
= ctx
->Query
.CurrentTimerObject
;
322 ctx
->Query
.CurrentTimerObject
= NULL
;
325 _mesa_error(ctx
, GL_INVALID_ENUM
, "glEndQueryARB(target)");
329 if (!q
|| !q
->Active
) {
330 _mesa_error(ctx
, GL_INVALID_OPERATION
,
331 "glEndQueryARB(no matching glBeginQueryARB)");
335 q
->Active
= GL_FALSE
;
336 ctx
->Driver
.EndQuery(ctx
, q
);
341 _mesa_GetQueryivARB(GLenum target
, GLenum pname
, GLint
*params
)
343 struct gl_query_object
*q
;
344 GET_CURRENT_CONTEXT(ctx
);
345 ASSERT_OUTSIDE_BEGIN_END(ctx
);
348 case GL_SAMPLES_PASSED_ARB
:
349 if (!ctx
->Extensions
.ARB_occlusion_query
) {
350 _mesa_error(ctx
, GL_INVALID_ENUM
, "glEndQueryARB(target)");
353 q
= ctx
->Query
.CurrentOcclusionObject
;
355 case GL_TIME_ELAPSED_EXT
:
356 if (!ctx
->Extensions
.EXT_timer_query
) {
357 _mesa_error(ctx
, GL_INVALID_ENUM
, "glEndQueryARB(target)");
360 q
= ctx
->Query
.CurrentTimerObject
;
363 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGetQueryivARB(target)");
368 case GL_QUERY_COUNTER_BITS_ARB
:
369 *params
= 8 * sizeof(q
->Result
);
371 case GL_CURRENT_QUERY_ARB
:
372 *params
= q
? q
->Id
: 0;
375 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGetQueryivARB(pname)");
382 _mesa_GetQueryObjectivARB(GLuint id
, GLenum pname
, GLint
*params
)
384 struct gl_query_object
*q
= NULL
;
385 GET_CURRENT_CONTEXT(ctx
);
386 ASSERT_OUTSIDE_BEGIN_END(ctx
);
389 q
= lookup_query_object(ctx
, id
);
391 if (!q
|| q
->Active
) {
392 _mesa_error(ctx
, GL_INVALID_OPERATION
,
393 "glGetQueryObjectivARB(id=%d is invalid or active)", id
);
398 case GL_QUERY_RESULT_ARB
:
400 ctx
->Driver
.WaitQuery(ctx
, q
);
401 /* if result is too large for returned type, clamp to max value */
402 if (q
->Result
> 0x7fffffff) {
403 *params
= 0x7fffffff;
406 *params
= (GLint
)q
->Result
;
409 case GL_QUERY_RESULT_AVAILABLE_ARB
:
411 ctx
->Driver
.CheckQuery( ctx
, q
);
415 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGetQueryObjectivARB(pname)");
422 _mesa_GetQueryObjectuivARB(GLuint id
, GLenum pname
, GLuint
*params
)
424 struct gl_query_object
*q
= NULL
;
425 GET_CURRENT_CONTEXT(ctx
);
426 ASSERT_OUTSIDE_BEGIN_END(ctx
);
429 q
= lookup_query_object(ctx
, id
);
431 if (!q
|| q
->Active
) {
432 _mesa_error(ctx
, GL_INVALID_OPERATION
,
433 "glGetQueryObjectuivARB(id=%d is invalid or active)", id
);
438 case GL_QUERY_RESULT_ARB
:
440 ctx
->Driver
.WaitQuery(ctx
, q
);
441 /* if result is too large for returned type, clamp to max value */
442 if (q
->Result
> 0xffffffff) {
443 *params
= 0xffffffff;
446 *params
= (GLuint
)q
->Result
;
449 case GL_QUERY_RESULT_AVAILABLE_ARB
:
451 ctx
->Driver
.CheckQuery( ctx
, q
);
455 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGetQueryObjectuivARB(pname)");
462 * New with GL_EXT_timer_query
464 static void GLAPIENTRY
465 _mesa_GetQueryObjecti64vEXT(GLuint id
, GLenum pname
, GLint64EXT
*params
)
467 struct gl_query_object
*q
= NULL
;
468 GET_CURRENT_CONTEXT(ctx
);
469 ASSERT_OUTSIDE_BEGIN_END(ctx
);
472 q
= lookup_query_object(ctx
, id
);
474 if (!q
|| q
->Active
) {
475 _mesa_error(ctx
, GL_INVALID_OPERATION
,
476 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id
);
481 case GL_QUERY_RESULT_ARB
:
483 ctx
->Driver
.WaitQuery(ctx
, q
);
486 case GL_QUERY_RESULT_AVAILABLE_ARB
:
488 ctx
->Driver
.CheckQuery( ctx
, q
);
492 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGetQueryObjecti64vARB(pname)");
499 * New with GL_EXT_timer_query
501 static void GLAPIENTRY
502 _mesa_GetQueryObjectui64vEXT(GLuint id
, GLenum pname
, GLuint64EXT
*params
)
504 struct gl_query_object
*q
= NULL
;
505 GET_CURRENT_CONTEXT(ctx
);
506 ASSERT_OUTSIDE_BEGIN_END(ctx
);
509 q
= lookup_query_object(ctx
, id
);
511 if (!q
|| q
->Active
) {
512 _mesa_error(ctx
, GL_INVALID_OPERATION
,
513 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id
);
518 case GL_QUERY_RESULT_ARB
:
520 ctx
->Driver
.WaitQuery(ctx
, q
);
523 case GL_QUERY_RESULT_AVAILABLE_ARB
:
525 ctx
->Driver
.CheckQuery( ctx
, q
);
529 _mesa_error(ctx
, GL_INVALID_ENUM
, "glGetQueryObjectui64vARB(pname)");
536 _mesa_init_queryobj_dispatch(struct _glapi_table
*disp
)
538 SET_GenQueriesARB(disp
, _mesa_GenQueriesARB
);
539 SET_DeleteQueriesARB(disp
, _mesa_DeleteQueriesARB
);
540 SET_IsQueryARB(disp
, _mesa_IsQueryARB
);
541 SET_BeginQueryARB(disp
, _mesa_BeginQueryARB
);
542 SET_EndQueryARB(disp
, _mesa_EndQueryARB
);
543 SET_GetQueryivARB(disp
, _mesa_GetQueryivARB
);
544 SET_GetQueryObjectivARB(disp
, _mesa_GetQueryObjectivARB
);
545 SET_GetQueryObjectuivARB(disp
, _mesa_GetQueryObjectuivARB
);
547 SET_GetQueryObjecti64vEXT(disp
, _mesa_GetQueryObjecti64vEXT
);
548 SET_GetQueryObjectui64vEXT(disp
, _mesa_GetQueryObjectui64vEXT
);
552 #endif /* FEATURE_queryobj */
556 * Allocate/init the context state related to query objects.
559 _mesa_init_queryobj(GLcontext
*ctx
)
561 ctx
->Query
.QueryObjects
= _mesa_NewHashTable();
562 ctx
->Query
.CurrentOcclusionObject
= NULL
;
567 * Callback for deleting a query object. Called by _mesa_HashDeleteAll().
570 delete_queryobj_cb(GLuint id
, void *data
, void *userData
)
572 struct gl_query_object
*q
= (struct gl_query_object
*) data
;
573 GLcontext
*ctx
= (GLcontext
*)userData
;
574 ctx
->Driver
.DeleteQuery(ctx
, q
);
579 * Free the context state related to query objects.
582 _mesa_free_queryobj_data(GLcontext
*ctx
)
584 _mesa_HashDeleteAll(ctx
->Query
.QueryObjects
, delete_queryobj_cb
, ctx
);
585 _mesa_DeleteHashTable(ctx
->Query
.QueryObjects
);