queryobj.c revision ff7aa554a11863de2c4c0b4b6d1ec7b07c819739
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 "main/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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *ctx, struct gl_query_object *q) 116{ 117 free(q); 118} 119 120 121void 122_mesa_init_query_object_functions(struct dd_function_table *driver) 123{ 124 driver->NewQueryObject = _mesa_new_query_object; 125 driver->DeleteQuery = _mesa_delete_query; 126 driver->BeginQuery = _mesa_begin_query; 127 driver->EndQuery = _mesa_end_query; 128 driver->WaitQuery = _mesa_wait_query; 129 driver->CheckQuery = _mesa_check_query; 130} 131 132 133/** 134 * Return pointer to the query object binding point for the given target. 135 * \return NULL if invalid target, else the address of binding point 136 */ 137static struct gl_query_object ** 138get_query_binding_point(struct gl_context *ctx, GLenum target) 139{ 140 switch (target) { 141 case GL_SAMPLES_PASSED_ARB: 142 if (ctx->Extensions.ARB_occlusion_query) 143 return &ctx->Query.CurrentOcclusionObject; 144 else 145 return NULL; 146 case GL_ANY_SAMPLES_PASSED: 147 if (ctx->Extensions.ARB_occlusion_query2) 148 return &ctx->Query.CurrentOcclusionObject; 149 else 150 return NULL; 151 case GL_TIME_ELAPSED_EXT: 152 if (ctx->Extensions.EXT_timer_query) 153 return &ctx->Query.CurrentTimerObject; 154 else 155 return NULL; 156#if FEATURE_EXT_transform_feedback 157 case GL_PRIMITIVES_GENERATED: 158 if (ctx->Extensions.EXT_transform_feedback) 159 return &ctx->Query.PrimitivesGenerated; 160 else 161 return NULL; 162 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 163 if (ctx->Extensions.EXT_transform_feedback) 164 return &ctx->Query.PrimitivesWritten; 165 else 166 return NULL; 167#endif 168 default: 169 return NULL; 170 } 171} 172 173 174void GLAPIENTRY 175_mesa_GenQueriesARB(GLsizei n, GLuint *ids) 176{ 177 GLuint first; 178 GET_CURRENT_CONTEXT(ctx); 179 ASSERT_OUTSIDE_BEGIN_END(ctx); 180 181 if (n < 0) { 182 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)"); 183 return; 184 } 185 186 /* No query objects can be active at this time! */ 187 if (ctx->Query.CurrentOcclusionObject || 188 ctx->Query.CurrentTimerObject) { 189 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB"); 190 return; 191 } 192 193 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); 194 if (first) { 195 GLsizei i; 196 for (i = 0; i < n; i++) { 197 struct gl_query_object *q 198 = ctx->Driver.NewQueryObject(ctx, first + i); 199 if (!q) { 200 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB"); 201 return; 202 } 203 ids[i] = first + i; 204 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q); 205 } 206 } 207} 208 209 210void GLAPIENTRY 211_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids) 212{ 213 GLint i; 214 GET_CURRENT_CONTEXT(ctx); 215 ASSERT_OUTSIDE_BEGIN_END(ctx); 216 217 if (n < 0) { 218 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); 219 return; 220 } 221 222 /* No query objects can be active at this time! */ 223 if (ctx->Query.CurrentOcclusionObject || 224 ctx->Query.CurrentTimerObject) { 225 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB"); 226 return; 227 } 228 229 for (i = 0; i < n; i++) { 230 if (ids[i] > 0) { 231 struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]); 232 if (q) { 233 ASSERT(!q->Active); /* should be caught earlier */ 234 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]); 235 ctx->Driver.DeleteQuery(ctx, q); 236 } 237 } 238 } 239} 240 241 242GLboolean GLAPIENTRY 243_mesa_IsQueryARB(GLuint id) 244{ 245 GET_CURRENT_CONTEXT(ctx); 246 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 247 248 if (id && _mesa_lookup_query_object(ctx, id)) 249 return GL_TRUE; 250 else 251 return GL_FALSE; 252} 253 254 255static void GLAPIENTRY 256_mesa_BeginQueryARB(GLenum target, GLuint id) 257{ 258 struct gl_query_object *q, **bindpt; 259 GET_CURRENT_CONTEXT(ctx); 260 ASSERT_OUTSIDE_BEGIN_END(ctx); 261 262 FLUSH_VERTICES(ctx, _NEW_DEPTH); 263 264 bindpt = get_query_binding_point(ctx, target); 265 if (!bindpt) { 266 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); 267 return; 268 } 269 270 if (id == 0) { 271 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)"); 272 return; 273 } 274 275 q = _mesa_lookup_query_object(ctx, id); 276 if (!q) { 277 /* create new object */ 278 q = ctx->Driver.NewQueryObject(ctx, id); 279 if (!q) { 280 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB"); 281 return; 282 } 283 _mesa_HashInsert(ctx->Query.QueryObjects, id, q); 284 } 285 else { 286 /* pre-existing object */ 287 if (q->Active) { 288 _mesa_error(ctx, GL_INVALID_OPERATION, 289 "glBeginQueryARB(query already active)"); 290 return; 291 } 292 } 293 294 q->Target = target; 295 q->Active = GL_TRUE; 296 q->Result = 0; 297 q->Ready = GL_FALSE; 298 299 /* XXX should probably refcount query objects */ 300 *bindpt = q; 301 302 ctx->Driver.BeginQuery(ctx, q); 303} 304 305 306static void GLAPIENTRY 307_mesa_EndQueryARB(GLenum target) 308{ 309 struct gl_query_object *q, **bindpt; 310 GET_CURRENT_CONTEXT(ctx); 311 ASSERT_OUTSIDE_BEGIN_END(ctx); 312 313 FLUSH_VERTICES(ctx, _NEW_DEPTH); 314 315 bindpt = get_query_binding_point(ctx, target); 316 if (!bindpt) { 317 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); 318 return; 319 } 320 321 /* XXX should probably refcount query objects */ 322 q = *bindpt; 323 *bindpt = NULL; 324 325 if (!q || !q->Active) { 326 _mesa_error(ctx, GL_INVALID_OPERATION, 327 "glEndQueryARB(no matching glBeginQueryARB)"); 328 return; 329 } 330 331 q->Active = GL_FALSE; 332 ctx->Driver.EndQuery(ctx, q); 333} 334 335 336void GLAPIENTRY 337_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params) 338{ 339 struct gl_query_object *q, **bindpt; 340 GET_CURRENT_CONTEXT(ctx); 341 ASSERT_OUTSIDE_BEGIN_END(ctx); 342 343 bindpt = get_query_binding_point(ctx, target); 344 if (!bindpt) { 345 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)"); 346 return; 347 } 348 349 q = *bindpt; 350 351 switch (pname) { 352 case GL_QUERY_COUNTER_BITS_ARB: 353 *params = 8 * sizeof(q->Result); 354 break; 355 case GL_CURRENT_QUERY_ARB: 356 *params = q ? q->Id : 0; 357 break; 358 default: 359 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)"); 360 return; 361 } 362} 363 364 365void GLAPIENTRY 366_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params) 367{ 368 struct gl_query_object *q = NULL; 369 GET_CURRENT_CONTEXT(ctx); 370 ASSERT_OUTSIDE_BEGIN_END(ctx); 371 372 if (id) 373 q = _mesa_lookup_query_object(ctx, id); 374 375 if (!q || q->Active) { 376 _mesa_error(ctx, GL_INVALID_OPERATION, 377 "glGetQueryObjectivARB(id=%d is invalid or active)", id); 378 return; 379 } 380 381 switch (pname) { 382 case GL_QUERY_RESULT_ARB: 383 if (!q->Ready) 384 ctx->Driver.WaitQuery(ctx, q); 385 /* if result is too large for returned type, clamp to max value */ 386 if (q->Target == GL_ANY_SAMPLES_PASSED) { 387 if (q->Result) 388 *params = GL_TRUE; 389 else 390 *params = GL_FALSE; 391 } else { 392 if (q->Result > 0x7fffffff) { 393 *params = 0x7fffffff; 394 } 395 else { 396 *params = (GLint)q->Result; 397 } 398 } 399 break; 400 case GL_QUERY_RESULT_AVAILABLE_ARB: 401 if (!q->Ready) 402 ctx->Driver.CheckQuery( ctx, q ); 403 *params = q->Ready; 404 break; 405 default: 406 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)"); 407 return; 408 } 409} 410 411 412void GLAPIENTRY 413_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params) 414{ 415 struct gl_query_object *q = NULL; 416 GET_CURRENT_CONTEXT(ctx); 417 ASSERT_OUTSIDE_BEGIN_END(ctx); 418 419 if (id) 420 q = _mesa_lookup_query_object(ctx, id); 421 422 if (!q || q->Active) { 423 _mesa_error(ctx, GL_INVALID_OPERATION, 424 "glGetQueryObjectuivARB(id=%d is invalid or active)", id); 425 return; 426 } 427 428 switch (pname) { 429 case GL_QUERY_RESULT_ARB: 430 if (!q->Ready) 431 ctx->Driver.WaitQuery(ctx, q); 432 /* if result is too large for returned type, clamp to max value */ 433 if (q->Target == GL_ANY_SAMPLES_PASSED) { 434 if (q->Result) 435 *params = GL_TRUE; 436 else 437 *params = GL_FALSE; 438 } else { 439 if (q->Result > 0xffffffff) { 440 *params = 0xffffffff; 441 } 442 else { 443 *params = (GLuint)q->Result; 444 } 445 } 446 break; 447 case GL_QUERY_RESULT_AVAILABLE_ARB: 448 if (!q->Ready) 449 ctx->Driver.CheckQuery( ctx, q ); 450 *params = q->Ready; 451 break; 452 default: 453 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)"); 454 return; 455 } 456} 457 458 459/** 460 * New with GL_EXT_timer_query 461 */ 462static void GLAPIENTRY 463_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params) 464{ 465 struct gl_query_object *q = NULL; 466 GET_CURRENT_CONTEXT(ctx); 467 ASSERT_OUTSIDE_BEGIN_END(ctx); 468 469 if (id) 470 q = _mesa_lookup_query_object(ctx, id); 471 472 if (!q || q->Active) { 473 _mesa_error(ctx, GL_INVALID_OPERATION, 474 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id); 475 return; 476 } 477 478 switch (pname) { 479 case GL_QUERY_RESULT_ARB: 480 if (!q->Ready) 481 ctx->Driver.WaitQuery(ctx, q); 482 *params = q->Result; 483 break; 484 case GL_QUERY_RESULT_AVAILABLE_ARB: 485 if (!q->Ready) 486 ctx->Driver.CheckQuery( ctx, q ); 487 *params = q->Ready; 488 break; 489 default: 490 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)"); 491 return; 492 } 493} 494 495 496/** 497 * New with GL_EXT_timer_query 498 */ 499static void GLAPIENTRY 500_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params) 501{ 502 struct gl_query_object *q = NULL; 503 GET_CURRENT_CONTEXT(ctx); 504 ASSERT_OUTSIDE_BEGIN_END(ctx); 505 506 if (id) 507 q = _mesa_lookup_query_object(ctx, id); 508 509 if (!q || q->Active) { 510 _mesa_error(ctx, GL_INVALID_OPERATION, 511 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id); 512 return; 513 } 514 515 switch (pname) { 516 case GL_QUERY_RESULT_ARB: 517 if (!q->Ready) 518 ctx->Driver.WaitQuery(ctx, q); 519 *params = q->Result; 520 break; 521 case GL_QUERY_RESULT_AVAILABLE_ARB: 522 if (!q->Ready) 523 ctx->Driver.CheckQuery( ctx, q ); 524 *params = q->Ready; 525 break; 526 default: 527 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)"); 528 return; 529 } 530} 531 532 533void 534_mesa_init_queryobj_dispatch(struct _glapi_table *disp) 535{ 536 SET_GenQueriesARB(disp, _mesa_GenQueriesARB); 537 SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB); 538 SET_IsQueryARB(disp, _mesa_IsQueryARB); 539 SET_BeginQueryARB(disp, _mesa_BeginQueryARB); 540 SET_EndQueryARB(disp, _mesa_EndQueryARB); 541 SET_GetQueryivARB(disp, _mesa_GetQueryivARB); 542 SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB); 543 SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB); 544 545 SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT); 546 SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT); 547} 548 549 550#endif /* FEATURE_queryobj */ 551 552 553/** 554 * Allocate/init the context state related to query objects. 555 */ 556void 557_mesa_init_queryobj(struct gl_context *ctx) 558{ 559 ctx->Query.QueryObjects = _mesa_NewHashTable(); 560 ctx->Query.CurrentOcclusionObject = NULL; 561} 562 563 564/** 565 * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). 566 */ 567static void 568delete_queryobj_cb(GLuint id, void *data, void *userData) 569{ 570 struct gl_query_object *q= (struct gl_query_object *) data; 571 struct gl_context *ctx = (struct gl_context *)userData; 572 ctx->Driver.DeleteQuery(ctx, q); 573} 574 575 576/** 577 * Free the context state related to query objects. 578 */ 579void 580_mesa_free_queryobj_data(struct gl_context *ctx) 581{ 582 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); 583 _mesa_DeleteHashTable(ctx->Query.QueryObjects); 584} 585