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