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