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