queryobj.c revision c1f859d4
1/* 2 * Mesa 3-D graphics library 3 * Version: 7.1 4 * 5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 6 * 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: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 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. 23 */ 24 25 26#include "glheader.h" 27#include "context.h" 28#include "hash.h" 29#include "imports.h" 30#include "queryobj.h" 31#include "mtypes.h" 32 33 34/** 35 * Allocate a new query object. This is a fallback routine called via 36 * ctx->Driver.NewQueryObject(). 37 * \param ctx - rendering context 38 * \param id - the new object's ID 39 * \return pointer to new query_object object or NULL if out of memory. 40 */ 41struct gl_query_object * 42_mesa_new_query_object(GLcontext *ctx, GLuint id) 43{ 44 struct gl_query_object *q = MALLOC_STRUCT(gl_query_object); 45 (void) ctx; 46 if (q) { 47 q->Id = id; 48 q->Result = 0; 49 q->Active = GL_FALSE; 50 q->Ready = GL_TRUE; /* correct, see spec */ 51 } 52 return q; 53} 54 55 56/** 57 * Begin a query. Software driver fallback. 58 * Called via ctx->Driver.BeginQuery(). 59 */ 60void 61_mesa_begin_query(GLcontext *ctx, struct gl_query_object *q) 62{ 63 /* no-op */ 64} 65 66 67/** 68 * End a query. Software driver fallback. 69 * Called via ctx->Driver.EndQuery(). 70 */ 71void 72_mesa_end_query(GLcontext *ctx, struct gl_query_object *q) 73{ 74 q->Ready = GL_TRUE; 75} 76 77 78/** 79 * Wait for query to complete. Software driver fallback. 80 * Called via ctx->Driver.WaitQuery(). 81 */ 82void 83_mesa_wait_query(GLcontext *ctx, struct gl_query_object *q) 84{ 85 /* For software drivers, _mesa_end_query() should have completed the query. 86 * For real hardware, implement a proper WaitQuery() driver function. 87 */ 88 assert(q->Ready); 89} 90 91 92/** 93 * Delete a query object. Called via ctx->Driver.DeleteQuery(). 94 * Not removed from hash table here. 95 */ 96void 97_mesa_delete_query(GLcontext *ctx, struct gl_query_object *q) 98{ 99 _mesa_free(q); 100} 101 102 103static struct gl_query_object * 104lookup_query_object(GLcontext *ctx, GLuint id) 105{ 106 return (struct gl_query_object *) 107 _mesa_HashLookup(ctx->Query.QueryObjects, id); 108} 109 110 111 112void GLAPIENTRY 113_mesa_GenQueriesARB(GLsizei n, GLuint *ids) 114{ 115 GLuint first; 116 GET_CURRENT_CONTEXT(ctx); 117 ASSERT_OUTSIDE_BEGIN_END(ctx); 118 119 if (n < 0) { 120 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)"); 121 return; 122 } 123 124 /* No query objects can be active at this time! */ 125 if (ctx->Query.CurrentOcclusionObject || 126 ctx->Query.CurrentTimerObject) { 127 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB"); 128 return; 129 } 130 131 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); 132 if (first) { 133 GLsizei i; 134 for (i = 0; i < n; i++) { 135 struct gl_query_object *q 136 = ctx->Driver.NewQueryObject(ctx, first + i); 137 if (!q) { 138 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB"); 139 return; 140 } 141 ids[i] = first + i; 142 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q); 143 } 144 } 145} 146 147 148void GLAPIENTRY 149_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids) 150{ 151 GLint i; 152 GET_CURRENT_CONTEXT(ctx); 153 ASSERT_OUTSIDE_BEGIN_END(ctx); 154 155 if (n < 0) { 156 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); 157 return; 158 } 159 160 /* No query objects can be active at this time! */ 161 if (ctx->Query.CurrentOcclusionObject || 162 ctx->Query.CurrentTimerObject) { 163 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB"); 164 return; 165 } 166 167 for (i = 0; i < n; i++) { 168 if (ids[i] > 0) { 169 struct gl_query_object *q = lookup_query_object(ctx, ids[i]); 170 if (q) { 171 ASSERT(!q->Active); /* should be caught earlier */ 172 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]); 173 ctx->Driver.DeleteQuery(ctx, q); 174 } 175 } 176 } 177} 178 179 180GLboolean GLAPIENTRY 181_mesa_IsQueryARB(GLuint id) 182{ 183 GET_CURRENT_CONTEXT(ctx); 184 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 185 186 if (id && lookup_query_object(ctx, id)) 187 return GL_TRUE; 188 else 189 return GL_FALSE; 190} 191 192 193void GLAPIENTRY 194_mesa_BeginQueryARB(GLenum target, GLuint id) 195{ 196 struct gl_query_object *q; 197 GET_CURRENT_CONTEXT(ctx); 198 ASSERT_OUTSIDE_BEGIN_END(ctx); 199 200 FLUSH_VERTICES(ctx, _NEW_DEPTH); 201 202 switch (target) { 203 case GL_SAMPLES_PASSED_ARB: 204 if (!ctx->Extensions.ARB_occlusion_query) { 205 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 206 return; 207 } 208 if (ctx->Query.CurrentOcclusionObject) { 209 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB"); 210 return; 211 } 212 break; 213#if FEATURE_EXT_timer_query 214 case GL_TIME_ELAPSED_EXT: 215 if (!ctx->Extensions.EXT_timer_query) { 216 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 217 return; 218 } 219 if (ctx->Query.CurrentTimerObject) { 220 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB"); 221 return; 222 } 223 break; 224#endif 225 default: 226 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 227 return; 228 } 229 230 if (id == 0) { 231 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)"); 232 return; 233 } 234 235 q = lookup_query_object(ctx, id); 236 if (!q) { 237 /* create new object */ 238 q = ctx->Driver.NewQueryObject(ctx, id); 239 if (!q) { 240 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB"); 241 return; 242 } 243 _mesa_HashInsert(ctx->Query.QueryObjects, id, q); 244 } 245 else { 246 /* pre-existing object */ 247 if (q->Active) { 248 _mesa_error(ctx, GL_INVALID_OPERATION, 249 "glBeginQueryARB(query already active)"); 250 return; 251 } 252 } 253 254 q->Target = target; 255 q->Active = GL_TRUE; 256 q->Result = 0; 257 q->Ready = GL_FALSE; 258 259 if (target == GL_SAMPLES_PASSED_ARB) { 260 ctx->Query.CurrentOcclusionObject = q; 261 } 262#if FEATURE_EXT_timer_query 263 else if (target == GL_TIME_ELAPSED_EXT) { 264 ctx->Query.CurrentTimerObject = q; 265 } 266#endif 267 268 ctx->Driver.BeginQuery(ctx, q); 269} 270 271 272void GLAPIENTRY 273_mesa_EndQueryARB(GLenum target) 274{ 275 struct gl_query_object *q; 276 GET_CURRENT_CONTEXT(ctx); 277 ASSERT_OUTSIDE_BEGIN_END(ctx); 278 279 FLUSH_VERTICES(ctx, _NEW_DEPTH); 280 281 switch (target) { 282 case GL_SAMPLES_PASSED_ARB: 283 if (!ctx->Extensions.ARB_occlusion_query) { 284 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 285 return; 286 } 287 q = ctx->Query.CurrentOcclusionObject; 288 ctx->Query.CurrentOcclusionObject = NULL; 289 break; 290#if FEATURE_EXT_timer_query 291 case GL_TIME_ELAPSED_EXT: 292 if (!ctx->Extensions.EXT_timer_query) { 293 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 294 return; 295 } 296 q = ctx->Query.CurrentTimerObject; 297 ctx->Query.CurrentTimerObject = NULL; 298 break; 299#endif 300 default: 301 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 302 return; 303 } 304 305 if (!q || !q->Active) { 306 _mesa_error(ctx, GL_INVALID_OPERATION, 307 "glEndQueryARB(no matching glBeginQueryARB)"); 308 return; 309 } 310 311 q->Active = GL_FALSE; 312 ctx->Driver.EndQuery(ctx, q); 313} 314 315 316void GLAPIENTRY 317_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params) 318{ 319 struct gl_query_object *q; 320 GET_CURRENT_CONTEXT(ctx); 321 ASSERT_OUTSIDE_BEGIN_END(ctx); 322 323 switch (target) { 324 case GL_SAMPLES_PASSED_ARB: 325 if (!ctx->Extensions.ARB_occlusion_query) { 326 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 327 return; 328 } 329 q = ctx->Query.CurrentOcclusionObject; 330 break; 331#if FEATURE_EXT_timer_query 332 case GL_TIME_ELAPSED_EXT: 333 if (!ctx->Extensions.EXT_timer_query) { 334 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 335 return; 336 } 337 q = ctx->Query.CurrentTimerObject; 338 break; 339#endif 340 default: 341 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)"); 342 return; 343 } 344 345 switch (pname) { 346 case GL_QUERY_COUNTER_BITS_ARB: 347 *params = 8 * sizeof(q->Result); 348 break; 349 case GL_CURRENT_QUERY_ARB: 350 *params = q ? q->Id : 0; 351 break; 352 default: 353 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)"); 354 return; 355 } 356} 357 358 359void GLAPIENTRY 360_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params) 361{ 362 struct gl_query_object *q = NULL; 363 GET_CURRENT_CONTEXT(ctx); 364 ASSERT_OUTSIDE_BEGIN_END(ctx); 365 366 if (id) 367 q = lookup_query_object(ctx, id); 368 369 if (!q || q->Active) { 370 _mesa_error(ctx, GL_INVALID_OPERATION, 371 "glGetQueryObjectivARB(id=%d is invalid or active)", id); 372 return; 373 } 374 375 switch (pname) { 376 case GL_QUERY_RESULT_ARB: 377 if (!q->Ready) 378 ctx->Driver.WaitQuery(ctx, q); 379 /* if result is too large for returned type, clamp to max value */ 380 if (q->Result > 0x7fffffff) { 381 *params = 0x7fffffff; 382 } 383 else { 384 *params = (GLint)q->Result; 385 } 386 break; 387 case GL_QUERY_RESULT_AVAILABLE_ARB: 388 if (!q->Ready) 389 ctx->Driver.CheckQuery( ctx, q ); 390 *params = q->Ready; 391 break; 392 default: 393 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)"); 394 return; 395 } 396} 397 398 399void GLAPIENTRY 400_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params) 401{ 402 struct gl_query_object *q = NULL; 403 GET_CURRENT_CONTEXT(ctx); 404 ASSERT_OUTSIDE_BEGIN_END(ctx); 405 406 if (id) 407 q = lookup_query_object(ctx, id); 408 409 if (!q || q->Active) { 410 _mesa_error(ctx, GL_INVALID_OPERATION, 411 "glGetQueryObjectuivARB(id=%d is invalid or active)", id); 412 return; 413 } 414 415 switch (pname) { 416 case GL_QUERY_RESULT_ARB: 417 if (!q->Ready) 418 ctx->Driver.WaitQuery(ctx, q); 419 /* if result is too large for returned type, clamp to max value */ 420 if (q->Result > 0xffffffff) { 421 *params = 0xffffffff; 422 } 423 else { 424 *params = (GLuint)q->Result; 425 } 426 break; 427 case GL_QUERY_RESULT_AVAILABLE_ARB: 428 if (!q->Ready) 429 ctx->Driver.CheckQuery( ctx, q ); 430 *params = q->Ready; 431 break; 432 default: 433 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)"); 434 return; 435 } 436} 437 438 439#if FEATURE_EXT_timer_query 440 441/** 442 * New with GL_EXT_timer_query 443 */ 444void GLAPIENTRY 445_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params) 446{ 447 struct gl_query_object *q = NULL; 448 GET_CURRENT_CONTEXT(ctx); 449 ASSERT_OUTSIDE_BEGIN_END(ctx); 450 451 if (id) 452 q = lookup_query_object(ctx, id); 453 454 if (!q || q->Active) { 455 _mesa_error(ctx, GL_INVALID_OPERATION, 456 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id); 457 return; 458 } 459 460 switch (pname) { 461 case GL_QUERY_RESULT_ARB: 462 if (!q->Ready) 463 ctx->Driver.WaitQuery(ctx, q); 464 *params = q->Result; 465 break; 466 case GL_QUERY_RESULT_AVAILABLE_ARB: 467 if (!q->Ready) 468 ctx->Driver.CheckQuery( ctx, q ); 469 *params = q->Ready; 470 break; 471 default: 472 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)"); 473 return; 474 } 475} 476 477 478/** 479 * New with GL_EXT_timer_query 480 */ 481void GLAPIENTRY 482_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params) 483{ 484 struct gl_query_object *q = NULL; 485 GET_CURRENT_CONTEXT(ctx); 486 ASSERT_OUTSIDE_BEGIN_END(ctx); 487 488 if (id) 489 q = lookup_query_object(ctx, id); 490 491 if (!q || q->Active) { 492 _mesa_error(ctx, GL_INVALID_OPERATION, 493 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id); 494 return; 495 } 496 497 switch (pname) { 498 case GL_QUERY_RESULT_ARB: 499 if (!q->Ready) 500 ctx->Driver.WaitQuery(ctx, q); 501 *params = q->Result; 502 break; 503 case GL_QUERY_RESULT_AVAILABLE_ARB: 504 if (!q->Ready) 505 ctx->Driver.CheckQuery( ctx, q ); 506 *params = q->Ready; 507 break; 508 default: 509 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)"); 510 return; 511 } 512} 513 514#endif /* FEATURE_EXT_timer_query */ 515 516 517/** 518 * Allocate/init the context state related to query objects. 519 */ 520void 521_mesa_init_query(GLcontext *ctx) 522{ 523#if FEATURE_ARB_occlusion_query 524 ctx->Query.QueryObjects = _mesa_NewHashTable(); 525 ctx->Query.CurrentOcclusionObject = NULL; 526#endif 527} 528 529 530/** 531 * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). 532 */ 533static void 534delete_queryobj_cb(GLuint id, void *data, void *userData) 535{ 536 struct gl_query_object *q= (struct gl_query_object *) data; 537 GLcontext *ctx = (GLcontext *)userData; 538 ctx->Driver.DeleteQuery(ctx, q); 539} 540 541 542/** 543 * Free the context state related to query objects. 544 */ 545void 546_mesa_free_query_data(GLcontext *ctx) 547{ 548 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); 549 _mesa_DeleteHashTable(ctx->Query.QueryObjects); 550} 551