queryobj.c revision 4a49301e
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#include "glapi/dispatch.h" 33 34 35#if FEATURE_queryobj 36 37 38/** 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. 44 */ 45static struct gl_query_object * 46_mesa_new_query_object(GLcontext *ctx, GLuint id) 47{ 48 struct gl_query_object *q = MALLOC_STRUCT(gl_query_object); 49 (void) ctx; 50 if (q) { 51 q->Id = id; 52 q->Result = 0; 53 q->Active = GL_FALSE; 54 q->Ready = GL_TRUE; /* correct, see spec */ 55 } 56 return q; 57} 58 59 60/** 61 * Begin a query. Software driver fallback. 62 * Called via ctx->Driver.BeginQuery(). 63 */ 64static void 65_mesa_begin_query(GLcontext *ctx, struct gl_query_object *q) 66{ 67 /* no-op */ 68} 69 70 71/** 72 * End a query. Software driver fallback. 73 * Called via ctx->Driver.EndQuery(). 74 */ 75static void 76_mesa_end_query(GLcontext *ctx, struct gl_query_object *q) 77{ 78 q->Ready = GL_TRUE; 79} 80 81 82/** 83 * Wait for query to complete. Software driver fallback. 84 * Called via ctx->Driver.WaitQuery(). 85 */ 86static void 87_mesa_wait_query(GLcontext *ctx, struct gl_query_object *q) 88{ 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. 92 */ 93 assert(q->Ready); 94} 95 96 97/** 98 * Check if a query results are ready. Software driver fallback. 99 * Called via ctx->Driver.CheckQuery(). 100 */ 101static void 102_mesa_check_query(GLcontext *ctx, struct gl_query_object *q) 103{ 104 /* No-op for sw rendering. 105 * HW drivers may need to flush at this time. 106 */ 107} 108 109 110/** 111 * Delete a query object. Called via ctx->Driver.DeleteQuery(). 112 * Not removed from hash table here. 113 */ 114static void 115_mesa_delete_query(GLcontext *ctx, struct gl_query_object *q) 116{ 117 _mesa_free(q); 118} 119 120 121static struct gl_query_object * 122lookup_query_object(GLcontext *ctx, GLuint id) 123{ 124 return (struct gl_query_object *) 125 _mesa_HashLookup(ctx->Query.QueryObjects, id); 126} 127 128 129 130void 131_mesa_init_query_object_functions(struct dd_function_table *driver) 132{ 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; 139} 140 141 142void GLAPIENTRY 143_mesa_GenQueriesARB(GLsizei n, GLuint *ids) 144{ 145 GLuint first; 146 GET_CURRENT_CONTEXT(ctx); 147 ASSERT_OUTSIDE_BEGIN_END(ctx); 148 149 if (n < 0) { 150 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)"); 151 return; 152 } 153 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"); 158 return; 159 } 160 161 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); 162 if (first) { 163 GLsizei i; 164 for (i = 0; i < n; i++) { 165 struct gl_query_object *q 166 = ctx->Driver.NewQueryObject(ctx, first + i); 167 if (!q) { 168 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB"); 169 return; 170 } 171 ids[i] = first + i; 172 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q); 173 } 174 } 175} 176 177 178void GLAPIENTRY 179_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids) 180{ 181 GLint i; 182 GET_CURRENT_CONTEXT(ctx); 183 ASSERT_OUTSIDE_BEGIN_END(ctx); 184 185 if (n < 0) { 186 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); 187 return; 188 } 189 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"); 194 return; 195 } 196 197 for (i = 0; i < n; i++) { 198 if (ids[i] > 0) { 199 struct gl_query_object *q = lookup_query_object(ctx, ids[i]); 200 if (q) { 201 ASSERT(!q->Active); /* should be caught earlier */ 202 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]); 203 ctx->Driver.DeleteQuery(ctx, q); 204 } 205 } 206 } 207} 208 209 210GLboolean GLAPIENTRY 211_mesa_IsQueryARB(GLuint id) 212{ 213 GET_CURRENT_CONTEXT(ctx); 214 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 215 216 if (id && lookup_query_object(ctx, id)) 217 return GL_TRUE; 218 else 219 return GL_FALSE; 220} 221 222 223static void GLAPIENTRY 224_mesa_BeginQueryARB(GLenum target, GLuint id) 225{ 226 struct gl_query_object *q; 227 GET_CURRENT_CONTEXT(ctx); 228 ASSERT_OUTSIDE_BEGIN_END(ctx); 229 230 FLUSH_VERTICES(ctx, _NEW_DEPTH); 231 232 switch (target) { 233 case GL_SAMPLES_PASSED_ARB: 234 if (!ctx->Extensions.ARB_occlusion_query) { 235 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 236 return; 237 } 238 if (ctx->Query.CurrentOcclusionObject) { 239 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB"); 240 return; 241 } 242 break; 243 case GL_TIME_ELAPSED_EXT: 244 if (!ctx->Extensions.EXT_timer_query) { 245 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 246 return; 247 } 248 if (ctx->Query.CurrentTimerObject) { 249 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB"); 250 return; 251 } 252 break; 253 default: 254 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 255 return; 256 } 257 258 if (id == 0) { 259 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)"); 260 return; 261 } 262 263 q = lookup_query_object(ctx, id); 264 if (!q) { 265 /* create new object */ 266 q = ctx->Driver.NewQueryObject(ctx, id); 267 if (!q) { 268 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB"); 269 return; 270 } 271 _mesa_HashInsert(ctx->Query.QueryObjects, id, q); 272 } 273 else { 274 /* pre-existing object */ 275 if (q->Active) { 276 _mesa_error(ctx, GL_INVALID_OPERATION, 277 "glBeginQueryARB(query already active)"); 278 return; 279 } 280 } 281 282 q->Target = target; 283 q->Active = GL_TRUE; 284 q->Result = 0; 285 q->Ready = GL_FALSE; 286 287 if (target == GL_SAMPLES_PASSED_ARB) { 288 ctx->Query.CurrentOcclusionObject = q; 289 } 290 else if (target == GL_TIME_ELAPSED_EXT) { 291 ctx->Query.CurrentTimerObject = q; 292 } 293 294 ctx->Driver.BeginQuery(ctx, q); 295} 296 297 298static void GLAPIENTRY 299_mesa_EndQueryARB(GLenum target) 300{ 301 struct gl_query_object *q; 302 GET_CURRENT_CONTEXT(ctx); 303 ASSERT_OUTSIDE_BEGIN_END(ctx); 304 305 FLUSH_VERTICES(ctx, _NEW_DEPTH); 306 307 switch (target) { 308 case GL_SAMPLES_PASSED_ARB: 309 if (!ctx->Extensions.ARB_occlusion_query) { 310 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 311 return; 312 } 313 q = ctx->Query.CurrentOcclusionObject; 314 ctx->Query.CurrentOcclusionObject = NULL; 315 break; 316 case GL_TIME_ELAPSED_EXT: 317 if (!ctx->Extensions.EXT_timer_query) { 318 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 319 return; 320 } 321 q = ctx->Query.CurrentTimerObject; 322 ctx->Query.CurrentTimerObject = NULL; 323 break; 324 default: 325 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 326 return; 327 } 328 329 if (!q || !q->Active) { 330 _mesa_error(ctx, GL_INVALID_OPERATION, 331 "glEndQueryARB(no matching glBeginQueryARB)"); 332 return; 333 } 334 335 q->Active = GL_FALSE; 336 ctx->Driver.EndQuery(ctx, q); 337} 338 339 340void GLAPIENTRY 341_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params) 342{ 343 struct gl_query_object *q; 344 GET_CURRENT_CONTEXT(ctx); 345 ASSERT_OUTSIDE_BEGIN_END(ctx); 346 347 switch (target) { 348 case GL_SAMPLES_PASSED_ARB: 349 if (!ctx->Extensions.ARB_occlusion_query) { 350 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 351 return; 352 } 353 q = ctx->Query.CurrentOcclusionObject; 354 break; 355 case GL_TIME_ELAPSED_EXT: 356 if (!ctx->Extensions.EXT_timer_query) { 357 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 358 return; 359 } 360 q = ctx->Query.CurrentTimerObject; 361 break; 362 default: 363 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)"); 364 return; 365 } 366 367 switch (pname) { 368 case GL_QUERY_COUNTER_BITS_ARB: 369 *params = 8 * sizeof(q->Result); 370 break; 371 case GL_CURRENT_QUERY_ARB: 372 *params = q ? q->Id : 0; 373 break; 374 default: 375 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)"); 376 return; 377 } 378} 379 380 381void GLAPIENTRY 382_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params) 383{ 384 struct gl_query_object *q = NULL; 385 GET_CURRENT_CONTEXT(ctx); 386 ASSERT_OUTSIDE_BEGIN_END(ctx); 387 388 if (id) 389 q = lookup_query_object(ctx, id); 390 391 if (!q || q->Active) { 392 _mesa_error(ctx, GL_INVALID_OPERATION, 393 "glGetQueryObjectivARB(id=%d is invalid or active)", id); 394 return; 395 } 396 397 switch (pname) { 398 case GL_QUERY_RESULT_ARB: 399 if (!q->Ready) 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; 404 } 405 else { 406 *params = (GLint)q->Result; 407 } 408 break; 409 case GL_QUERY_RESULT_AVAILABLE_ARB: 410 if (!q->Ready) 411 ctx->Driver.CheckQuery( ctx, q ); 412 *params = q->Ready; 413 break; 414 default: 415 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)"); 416 return; 417 } 418} 419 420 421void GLAPIENTRY 422_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params) 423{ 424 struct gl_query_object *q = NULL; 425 GET_CURRENT_CONTEXT(ctx); 426 ASSERT_OUTSIDE_BEGIN_END(ctx); 427 428 if (id) 429 q = lookup_query_object(ctx, id); 430 431 if (!q || q->Active) { 432 _mesa_error(ctx, GL_INVALID_OPERATION, 433 "glGetQueryObjectuivARB(id=%d is invalid or active)", id); 434 return; 435 } 436 437 switch (pname) { 438 case GL_QUERY_RESULT_ARB: 439 if (!q->Ready) 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; 444 } 445 else { 446 *params = (GLuint)q->Result; 447 } 448 break; 449 case GL_QUERY_RESULT_AVAILABLE_ARB: 450 if (!q->Ready) 451 ctx->Driver.CheckQuery( ctx, q ); 452 *params = q->Ready; 453 break; 454 default: 455 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)"); 456 return; 457 } 458} 459 460 461/** 462 * New with GL_EXT_timer_query 463 */ 464static void GLAPIENTRY 465_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params) 466{ 467 struct gl_query_object *q = NULL; 468 GET_CURRENT_CONTEXT(ctx); 469 ASSERT_OUTSIDE_BEGIN_END(ctx); 470 471 if (id) 472 q = lookup_query_object(ctx, id); 473 474 if (!q || q->Active) { 475 _mesa_error(ctx, GL_INVALID_OPERATION, 476 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id); 477 return; 478 } 479 480 switch (pname) { 481 case GL_QUERY_RESULT_ARB: 482 if (!q->Ready) 483 ctx->Driver.WaitQuery(ctx, q); 484 *params = q->Result; 485 break; 486 case GL_QUERY_RESULT_AVAILABLE_ARB: 487 if (!q->Ready) 488 ctx->Driver.CheckQuery( ctx, q ); 489 *params = q->Ready; 490 break; 491 default: 492 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)"); 493 return; 494 } 495} 496 497 498/** 499 * New with GL_EXT_timer_query 500 */ 501static void GLAPIENTRY 502_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params) 503{ 504 struct gl_query_object *q = NULL; 505 GET_CURRENT_CONTEXT(ctx); 506 ASSERT_OUTSIDE_BEGIN_END(ctx); 507 508 if (id) 509 q = lookup_query_object(ctx, id); 510 511 if (!q || q->Active) { 512 _mesa_error(ctx, GL_INVALID_OPERATION, 513 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id); 514 return; 515 } 516 517 switch (pname) { 518 case GL_QUERY_RESULT_ARB: 519 if (!q->Ready) 520 ctx->Driver.WaitQuery(ctx, q); 521 *params = q->Result; 522 break; 523 case GL_QUERY_RESULT_AVAILABLE_ARB: 524 if (!q->Ready) 525 ctx->Driver.CheckQuery( ctx, q ); 526 *params = q->Ready; 527 break; 528 default: 529 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)"); 530 return; 531 } 532} 533 534 535void 536_mesa_init_queryobj_dispatch(struct _glapi_table *disp) 537{ 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); 546 547 SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT); 548 SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT); 549} 550 551 552#endif /* FEATURE_queryobj */ 553 554 555/** 556 * Allocate/init the context state related to query objects. 557 */ 558void 559_mesa_init_queryobj(GLcontext *ctx) 560{ 561 ctx->Query.QueryObjects = _mesa_NewHashTable(); 562 ctx->Query.CurrentOcclusionObject = NULL; 563} 564 565 566/** 567 * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). 568 */ 569static void 570delete_queryobj_cb(GLuint id, void *data, void *userData) 571{ 572 struct gl_query_object *q= (struct gl_query_object *) data; 573 GLcontext *ctx = (GLcontext *)userData; 574 ctx->Driver.DeleteQuery(ctx, q); 575} 576 577 578/** 579 * Free the context state related to query objects. 580 */ 581void 582_mesa_free_queryobj_data(GLcontext *ctx) 583{ 584 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); 585 _mesa_DeleteHashTable(ctx->Query.QueryObjects); 586} 587