queryobj.c revision 01e04c3f
1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 26#include "bufferobj.h" 27#include "glheader.h" 28#include "context.h" 29#include "enums.h" 30#include "hash.h" 31#include "imports.h" 32#include "queryobj.h" 33#include "mtypes.h" 34 35 36/** 37 * Allocate a new query object. This is a fallback routine called via 38 * ctx->Driver.NewQueryObject(). 39 * \param ctx - rendering context 40 * \param id - the new object's ID 41 * \return pointer to new query_object object or NULL if out of memory. 42 */ 43static struct gl_query_object * 44_mesa_new_query_object(struct gl_context *ctx, GLuint id) 45{ 46 struct gl_query_object *q = CALLOC_STRUCT(gl_query_object); 47 (void) ctx; 48 if (q) { 49 q->Id = id; 50 q->Result = 0; 51 q->Active = GL_FALSE; 52 53 /* This is to satisfy the language of the specification: "In the initial 54 * state of a query object, the result is available" (OpenGL 3.1 § 55 * 2.13). 56 */ 57 q->Ready = GL_TRUE; 58 59 /* OpenGL 3.1 § 2.13 says about GenQueries, "These names are marked as 60 * used, but no object is associated with them until the first time they 61 * are used by BeginQuery." Since our implementation actually does 62 * allocate an object at this point, use a flag to indicate that this 63 * object has not yet been bound so should not be considered a query. 64 */ 65 q->EverBound = GL_FALSE; 66 } 67 return q; 68} 69 70 71/** 72 * Begin a query. Software driver fallback. 73 * Called via ctx->Driver.BeginQuery(). 74 */ 75static void 76_mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q) 77{ 78 ctx->NewState |= _NEW_DEPTH; /* for swrast */ 79} 80 81 82/** 83 * End a query. Software driver fallback. 84 * Called via ctx->Driver.EndQuery(). 85 */ 86static void 87_mesa_end_query(struct gl_context *ctx, struct gl_query_object *q) 88{ 89 ctx->NewState |= _NEW_DEPTH; /* for swrast */ 90 q->Ready = GL_TRUE; 91} 92 93 94/** 95 * Wait for query to complete. Software driver fallback. 96 * Called via ctx->Driver.WaitQuery(). 97 */ 98static void 99_mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q) 100{ 101 /* For software drivers, _mesa_end_query() should have completed the query. 102 * For real hardware, implement a proper WaitQuery() driver function, 103 * which may require issuing a flush. 104 */ 105 assert(q->Ready); 106} 107 108 109/** 110 * Check if a query results are ready. Software driver fallback. 111 * Called via ctx->Driver.CheckQuery(). 112 */ 113static void 114_mesa_check_query(struct gl_context *ctx, struct gl_query_object *q) 115{ 116 /* No-op for sw rendering. 117 * HW drivers may need to flush at this time. 118 */ 119} 120 121 122/** 123 * Delete a query object. Called via ctx->Driver.DeleteQuery(). 124 * Not removed from hash table here. 125 */ 126static void 127_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q) 128{ 129 free(q->Label); 130 free(q); 131} 132 133 134void 135_mesa_init_query_object_functions(struct dd_function_table *driver) 136{ 137 driver->NewQueryObject = _mesa_new_query_object; 138 driver->DeleteQuery = _mesa_delete_query; 139 driver->BeginQuery = _mesa_begin_query; 140 driver->EndQuery = _mesa_end_query; 141 driver->WaitQuery = _mesa_wait_query; 142 driver->CheckQuery = _mesa_check_query; 143} 144 145static struct gl_query_object ** 146get_pipe_stats_binding_point(struct gl_context *ctx, 147 GLenum target) 148{ 149 const int which = target - GL_VERTICES_SUBMITTED_ARB; 150 assert(which < MAX_PIPELINE_STATISTICS); 151 152 if (!_mesa_is_desktop_gl(ctx) || 153 !ctx->Extensions.ARB_pipeline_statistics_query) 154 return NULL; 155 156 return &ctx->Query.pipeline_stats[which]; 157} 158 159/** 160 * Return pointer to the query object binding point for the given target and 161 * index. 162 * \return NULL if invalid target, else the address of binding point 163 */ 164static struct gl_query_object ** 165get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index) 166{ 167 168 /* From GL_EXT_occlusion_query_boolean spec: 169 * 170 * "Accepted by the <target> parameter of BeginQueryEXT, EndQueryEXT, 171 * and GetQueryivEXT: 172 * 173 * ANY_SAMPLES_PASSED_EXT 0x8C2F 174 * ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A" 175 */ 176 if ((_mesa_is_gles(ctx) && ctx->Version == 20) && 177 (target != GL_ANY_SAMPLES_PASSED && 178 target != GL_ANY_SAMPLES_PASSED_CONSERVATIVE)) 179 return NULL; 180 181 switch (target) { 182 case GL_SAMPLES_PASSED_ARB: 183 if (ctx->Extensions.ARB_occlusion_query) 184 return &ctx->Query.CurrentOcclusionObject; 185 else 186 return NULL; 187 case GL_ANY_SAMPLES_PASSED: 188 if (ctx->Extensions.ARB_occlusion_query2) 189 return &ctx->Query.CurrentOcclusionObject; 190 else 191 return NULL; 192 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 193 if (ctx->Extensions.ARB_ES3_compatibility 194 || (ctx->API == API_OPENGLES2 && ctx->Version >= 30)) 195 return &ctx->Query.CurrentOcclusionObject; 196 else 197 return NULL; 198 case GL_TIME_ELAPSED_EXT: 199 if (ctx->Extensions.EXT_timer_query) 200 return &ctx->Query.CurrentTimerObject; 201 else 202 return NULL; 203 case GL_PRIMITIVES_GENERATED: 204 if (ctx->Extensions.EXT_transform_feedback) 205 return &ctx->Query.PrimitivesGenerated[index]; 206 else 207 return NULL; 208 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 209 if (ctx->Extensions.EXT_transform_feedback) 210 return &ctx->Query.PrimitivesWritten[index]; 211 else 212 return NULL; 213 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB: 214 if (ctx->Extensions.ARB_transform_feedback_overflow_query) 215 return &ctx->Query.TransformFeedbackOverflow[index]; 216 else 217 return NULL; 218 case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB: 219 if (ctx->Extensions.ARB_transform_feedback_overflow_query) 220 return &ctx->Query.TransformFeedbackOverflowAny; 221 else 222 return NULL; 223 224 case GL_VERTICES_SUBMITTED_ARB: 225 case GL_PRIMITIVES_SUBMITTED_ARB: 226 case GL_VERTEX_SHADER_INVOCATIONS_ARB: 227 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: 228 case GL_CLIPPING_INPUT_PRIMITIVES_ARB: 229 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: 230 return get_pipe_stats_binding_point(ctx, target); 231 232 case GL_GEOMETRY_SHADER_INVOCATIONS: 233 /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */ 234 target = GL_VERTICES_SUBMITTED_ARB + MAX_PIPELINE_STATISTICS - 1; 235 /* fallthrough */ 236 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: 237 if (_mesa_has_geometry_shaders(ctx)) 238 return get_pipe_stats_binding_point(ctx, target); 239 else 240 return NULL; 241 242 case GL_TESS_CONTROL_SHADER_PATCHES_ARB: 243 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: 244 if (_mesa_has_tessellation(ctx)) 245 return get_pipe_stats_binding_point(ctx, target); 246 else 247 return NULL; 248 249 case GL_COMPUTE_SHADER_INVOCATIONS_ARB: 250 if (_mesa_has_compute_shaders(ctx)) 251 return get_pipe_stats_binding_point(ctx, target); 252 else 253 return NULL; 254 255 default: 256 return NULL; 257 } 258} 259 260/** 261 * Create $n query objects and store them in *ids. Make them of type $target 262 * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries(). 263 */ 264static void 265create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids, 266 bool dsa) 267{ 268 const char *func = dsa ? "glGenQueries" : "glCreateQueries"; 269 GLuint first; 270 271 if (MESA_VERBOSE & VERBOSE_API) 272 _mesa_debug(ctx, "%s(%d)\n", func, n); 273 274 if (n < 0) { 275 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); 276 return; 277 } 278 279 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); 280 if (first) { 281 GLsizei i; 282 for (i = 0; i < n; i++) { 283 struct gl_query_object *q 284 = ctx->Driver.NewQueryObject(ctx, first + i); 285 if (!q) { 286 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 287 return; 288 } else if (dsa) { 289 /* Do the equivalent of binding the buffer with a target */ 290 q->Target = target; 291 q->EverBound = GL_TRUE; 292 } 293 ids[i] = first + i; 294 _mesa_HashInsertLocked(ctx->Query.QueryObjects, first + i, q); 295 } 296 } 297} 298 299void GLAPIENTRY 300_mesa_GenQueries(GLsizei n, GLuint *ids) 301{ 302 GET_CURRENT_CONTEXT(ctx); 303 create_queries(ctx, 0, n, ids, false); 304} 305 306void GLAPIENTRY 307_mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids) 308{ 309 GET_CURRENT_CONTEXT(ctx); 310 311 switch (target) { 312 case GL_SAMPLES_PASSED: 313 case GL_ANY_SAMPLES_PASSED: 314 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 315 case GL_TIME_ELAPSED: 316 case GL_TIMESTAMP: 317 case GL_PRIMITIVES_GENERATED: 318 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 319 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB: 320 case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB: 321 break; 322 default: 323 _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)", 324 _mesa_enum_to_string(target)); 325 return; 326 } 327 328 create_queries(ctx, target, n, ids, true); 329} 330 331 332void GLAPIENTRY 333_mesa_DeleteQueries(GLsizei n, const GLuint *ids) 334{ 335 GLint i; 336 GET_CURRENT_CONTEXT(ctx); 337 FLUSH_VERTICES(ctx, 0); 338 339 if (MESA_VERBOSE & VERBOSE_API) 340 _mesa_debug(ctx, "glDeleteQueries(%d)\n", n); 341 342 if (n < 0) { 343 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); 344 return; 345 } 346 347 for (i = 0; i < n; i++) { 348 if (ids[i] > 0) { 349 struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]); 350 if (q) { 351 if (q->Active) { 352 struct gl_query_object **bindpt; 353 bindpt = get_query_binding_point(ctx, q->Target, q->Stream); 354 assert(bindpt); /* Should be non-null for active q. */ 355 if (bindpt) { 356 *bindpt = NULL; 357 } 358 q->Active = GL_FALSE; 359 ctx->Driver.EndQuery(ctx, q); 360 } 361 _mesa_HashRemoveLocked(ctx->Query.QueryObjects, ids[i]); 362 ctx->Driver.DeleteQuery(ctx, q); 363 } 364 } 365 } 366} 367 368 369GLboolean GLAPIENTRY 370_mesa_IsQuery(GLuint id) 371{ 372 struct gl_query_object *q; 373 374 GET_CURRENT_CONTEXT(ctx); 375 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 376 377 if (MESA_VERBOSE & VERBOSE_API) 378 _mesa_debug(ctx, "glIsQuery(%u)\n", id); 379 380 if (id == 0) 381 return GL_FALSE; 382 383 q = _mesa_lookup_query_object(ctx, id); 384 if (q == NULL) 385 return GL_FALSE; 386 387 return q->EverBound; 388} 389 390static GLboolean 391query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index) 392{ 393 switch (target) { 394 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 395 case GL_PRIMITIVES_GENERATED: 396 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB: 397 if (index >= ctx->Const.MaxVertexStreams) { 398 _mesa_error(ctx, GL_INVALID_VALUE, 399 "glBeginQueryIndexed(index>=MaxVertexStreams)"); 400 return GL_FALSE; 401 } 402 break; 403 default: 404 if (index > 0) { 405 _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)"); 406 return GL_FALSE; 407 } 408 } 409 return GL_TRUE; 410} 411 412void GLAPIENTRY 413_mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id) 414{ 415 struct gl_query_object *q, **bindpt; 416 GET_CURRENT_CONTEXT(ctx); 417 418 if (MESA_VERBOSE & VERBOSE_API) 419 _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n", 420 _mesa_enum_to_string(target), index, id); 421 422 if (!query_error_check_index(ctx, target, index)) 423 return; 424 425 FLUSH_VERTICES(ctx, 0); 426 427 bindpt = get_query_binding_point(ctx, target, index); 428 if (!bindpt) { 429 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)"); 430 return; 431 } 432 433 /* From the GL_ARB_occlusion_query spec: 434 * 435 * "If BeginQueryARB is called while another query is already in 436 * progress with the same target, an INVALID_OPERATION error is 437 * generated." 438 */ 439 if (*bindpt) { 440 _mesa_error(ctx, GL_INVALID_OPERATION, 441 "glBeginQuery{Indexed}(target=%s is active)", 442 _mesa_enum_to_string(target)); 443 return; 444 } 445 446 if (id == 0) { 447 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)"); 448 return; 449 } 450 451 q = _mesa_lookup_query_object(ctx, id); 452 if (!q) { 453 if (ctx->API != API_OPENGL_COMPAT) { 454 _mesa_error(ctx, GL_INVALID_OPERATION, 455 "glBeginQuery{Indexed}(non-gen name)"); 456 return; 457 } else { 458 /* create new object */ 459 q = ctx->Driver.NewQueryObject(ctx, id); 460 if (!q) { 461 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}"); 462 return; 463 } 464 _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q); 465 } 466 } 467 else { 468 /* pre-existing object */ 469 if (q->Active) { 470 _mesa_error(ctx, GL_INVALID_OPERATION, 471 "glBeginQuery{Indexed}(query already active)"); 472 return; 473 } 474 475 /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4 476 * spec states: 477 * 478 * "BeginQuery generates an INVALID_OPERATION error if any of the 479 * following conditions hold: [...] id is the name of an 480 * existing query object whose type does not match target; [...] 481 * 482 * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY 483 * OBJECTS AND ASYNCHRONOUS QUERIES, page 43. 484 */ 485 if (q->EverBound && q->Target != target) { 486 _mesa_error(ctx, GL_INVALID_OPERATION, 487 "glBeginQuery{Indexed}(target mismatch)"); 488 return; 489 } 490 } 491 492 /* This possibly changes the target of a buffer allocated by 493 * CreateQueries. Issue 39) in the ARB_direct_state_access extension states 494 * the following: 495 * 496 * "CreateQueries adds a <target>, so strictly speaking the <target> 497 * command isn't needed for BeginQuery/EndQuery, but in the end, this also 498 * isn't a selector, so we decided not to change it." 499 * 500 * Updating the target of the query object should be acceptable, so let's 501 * do that. 502 */ 503 504 q->Target = target; 505 q->Active = GL_TRUE; 506 q->Result = 0; 507 q->Ready = GL_FALSE; 508 q->EverBound = GL_TRUE; 509 q->Stream = index; 510 511 /* XXX should probably refcount query objects */ 512 *bindpt = q; 513 514 ctx->Driver.BeginQuery(ctx, q); 515} 516 517 518void GLAPIENTRY 519_mesa_EndQueryIndexed(GLenum target, GLuint index) 520{ 521 struct gl_query_object *q, **bindpt; 522 GET_CURRENT_CONTEXT(ctx); 523 524 if (MESA_VERBOSE & VERBOSE_API) 525 _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n", 526 _mesa_enum_to_string(target), index); 527 528 if (!query_error_check_index(ctx, target, index)) 529 return; 530 531 FLUSH_VERTICES(ctx, 0); 532 533 bindpt = get_query_binding_point(ctx, target, index); 534 if (!bindpt) { 535 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)"); 536 return; 537 } 538 539 /* XXX should probably refcount query objects */ 540 q = *bindpt; 541 542 /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */ 543 if (q && q->Target != target) { 544 _mesa_error(ctx, GL_INVALID_OPERATION, 545 "glEndQuery(target=%s with active query of target %s)", 546 _mesa_enum_to_string(target), 547 _mesa_enum_to_string(q->Target)); 548 return; 549 } 550 551 *bindpt = NULL; 552 553 if (!q || !q->Active) { 554 _mesa_error(ctx, GL_INVALID_OPERATION, 555 "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})"); 556 return; 557 } 558 559 q->Active = GL_FALSE; 560 ctx->Driver.EndQuery(ctx, q); 561} 562 563void GLAPIENTRY 564_mesa_BeginQuery(GLenum target, GLuint id) 565{ 566 _mesa_BeginQueryIndexed(target, 0, id); 567} 568 569void GLAPIENTRY 570_mesa_EndQuery(GLenum target) 571{ 572 _mesa_EndQueryIndexed(target, 0); 573} 574 575void GLAPIENTRY 576_mesa_QueryCounter(GLuint id, GLenum target) 577{ 578 struct gl_query_object *q; 579 GET_CURRENT_CONTEXT(ctx); 580 581 if (MESA_VERBOSE & VERBOSE_API) 582 _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id, 583 _mesa_enum_to_string(target)); 584 585 /* error checking */ 586 if (target != GL_TIMESTAMP) { 587 _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)"); 588 return; 589 } 590 591 if (id == 0) { 592 _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)"); 593 return; 594 } 595 596 q = _mesa_lookup_query_object(ctx, id); 597 if (!q) { 598 /* XXX the Core profile should throw INVALID_OPERATION here */ 599 600 /* create new object */ 601 q = ctx->Driver.NewQueryObject(ctx, id); 602 if (!q) { 603 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter"); 604 return; 605 } 606 _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q); 607 } 608 else { 609 if (q->Target && q->Target != GL_TIMESTAMP) { 610 _mesa_error(ctx, GL_INVALID_OPERATION, 611 "glQueryCounter(id has an invalid target)"); 612 return; 613 } 614 } 615 616 if (q->Active) { 617 _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)"); 618 return; 619 } 620 621 /* This possibly changes the target of a buffer allocated by 622 * CreateQueries. Issue 39) in the ARB_direct_state_access extension states 623 * the following: 624 * 625 * "CreateQueries adds a <target>, so strictly speaking the <target> 626 * command isn't needed for BeginQuery/EndQuery, but in the end, this also 627 * isn't a selector, so we decided not to change it." 628 * 629 * Updating the target of the query object should be acceptable, so let's 630 * do that. 631 */ 632 633 q->Target = target; 634 q->Result = 0; 635 q->Ready = GL_FALSE; 636 q->EverBound = GL_TRUE; 637 638 if (ctx->Driver.QueryCounter) { 639 ctx->Driver.QueryCounter(ctx, q); 640 } else { 641 /* QueryCounter is implemented using EndQuery without BeginQuery 642 * in drivers. This is actually Direct3D and Gallium convention. 643 */ 644 ctx->Driver.EndQuery(ctx, q); 645 } 646} 647 648 649void GLAPIENTRY 650_mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname, 651 GLint *params) 652{ 653 struct gl_query_object *q = NULL, **bindpt = NULL; 654 GET_CURRENT_CONTEXT(ctx); 655 656 if (MESA_VERBOSE & VERBOSE_API) 657 _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n", 658 _mesa_enum_to_string(target), 659 index, 660 _mesa_enum_to_string(pname)); 661 662 if (!query_error_check_index(ctx, target, index)) 663 return; 664 665 /* From the GL_EXT_occlusion_query_boolean spec: 666 * 667 * "The error INVALID_ENUM is generated if GetQueryivEXT is called where 668 * <pname> is not CURRENT_QUERY_EXT." 669 * 670 * Same rule is present also in ES 3.2 spec. 671 */ 672 if (_mesa_is_gles(ctx) && pname != GL_CURRENT_QUERY) { 673 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivEXT(%s)", 674 _mesa_enum_to_string(pname)); 675 return; 676 } 677 678 if (target == GL_TIMESTAMP) { 679 if (!ctx->Extensions.ARB_timer_query) { 680 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)"); 681 return; 682 } 683 } 684 else { 685 bindpt = get_query_binding_point(ctx, target, index); 686 if (!bindpt) { 687 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)"); 688 return; 689 } 690 691 q = *bindpt; 692 } 693 694 switch (pname) { 695 case GL_QUERY_COUNTER_BITS_ARB: 696 switch (target) { 697 case GL_SAMPLES_PASSED: 698 *params = ctx->Const.QueryCounterBits.SamplesPassed; 699 break; 700 case GL_ANY_SAMPLES_PASSED: 701 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 702 /* The minimum value of this is 1 if it's nonzero, and the value 703 * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more 704 * bits. 705 */ 706 *params = 1; 707 break; 708 case GL_TIME_ELAPSED: 709 *params = ctx->Const.QueryCounterBits.TimeElapsed; 710 break; 711 case GL_TIMESTAMP: 712 *params = ctx->Const.QueryCounterBits.Timestamp; 713 break; 714 case GL_PRIMITIVES_GENERATED: 715 *params = ctx->Const.QueryCounterBits.PrimitivesGenerated; 716 break; 717 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 718 *params = ctx->Const.QueryCounterBits.PrimitivesWritten; 719 break; 720 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB: 721 case GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB: 722 /* The minimum value of this is 1 if it's nonzero, and the value 723 * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more 724 * bits. 725 */ 726 *params = 1; 727 break; 728 case GL_VERTICES_SUBMITTED_ARB: 729 *params = ctx->Const.QueryCounterBits.VerticesSubmitted; 730 break; 731 case GL_PRIMITIVES_SUBMITTED_ARB: 732 *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted; 733 break; 734 case GL_VERTEX_SHADER_INVOCATIONS_ARB: 735 *params = ctx->Const.QueryCounterBits.VsInvocations; 736 break; 737 case GL_TESS_CONTROL_SHADER_PATCHES_ARB: 738 *params = ctx->Const.QueryCounterBits.TessPatches; 739 break; 740 case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: 741 *params = ctx->Const.QueryCounterBits.TessInvocations; 742 break; 743 case GL_GEOMETRY_SHADER_INVOCATIONS: 744 *params = ctx->Const.QueryCounterBits.GsInvocations; 745 break; 746 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: 747 *params = ctx->Const.QueryCounterBits.GsPrimitives; 748 break; 749 case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: 750 *params = ctx->Const.QueryCounterBits.FsInvocations; 751 break; 752 case GL_COMPUTE_SHADER_INVOCATIONS_ARB: 753 *params = ctx->Const.QueryCounterBits.ComputeInvocations; 754 break; 755 case GL_CLIPPING_INPUT_PRIMITIVES_ARB: 756 *params = ctx->Const.QueryCounterBits.ClInPrimitives; 757 break; 758 case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: 759 *params = ctx->Const.QueryCounterBits.ClOutPrimitives; 760 break; 761 default: 762 _mesa_problem(ctx, 763 "Unknown target in glGetQueryIndexediv(target = %s)", 764 _mesa_enum_to_string(target)); 765 *params = 0; 766 break; 767 } 768 break; 769 case GL_CURRENT_QUERY_ARB: 770 *params = (q && q->Target == target) ? q->Id : 0; 771 break; 772 default: 773 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)"); 774 return; 775 } 776} 777 778void GLAPIENTRY 779_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params) 780{ 781 _mesa_GetQueryIndexediv(target, 0, pname, params); 782} 783 784static void 785get_query_object(struct gl_context *ctx, const char *func, 786 GLuint id, GLenum pname, GLenum ptype, 787 struct gl_buffer_object *buf, intptr_t offset) 788{ 789 struct gl_query_object *q = NULL; 790 uint64_t value; 791 792 if (MESA_VERBOSE & VERBOSE_API) 793 _mesa_debug(ctx, "%s(%u, %s)\n", func, id, 794 _mesa_enum_to_string(pname)); 795 796 if (id) 797 q = _mesa_lookup_query_object(ctx, id); 798 799 if (!q || q->Active || !q->EverBound) { 800 _mesa_error(ctx, GL_INVALID_OPERATION, 801 "%s(id=%d is invalid or active)", func, id); 802 return; 803 } 804 805 /* From GL_EXT_occlusion_query_boolean spec: 806 * 807 * "Accepted by the <pname> parameter of GetQueryObjectivEXT and 808 * GetQueryObjectuivEXT: 809 * 810 * QUERY_RESULT_EXT 0x8866 811 * QUERY_RESULT_AVAILABLE_EXT 0x8867" 812 * 813 * Same rule is present also in ES 3.2 spec. 814 */ 815 if (_mesa_is_gles(ctx) && 816 (pname != GL_QUERY_RESULT && pname != GL_QUERY_RESULT_AVAILABLE)) { 817 _mesa_error(ctx, GL_INVALID_ENUM, "%s(%s)", func, 818 _mesa_enum_to_string(pname)); 819 return; 820 } 821 822 if (buf && buf != ctx->Shared->NullBufferObj) { 823 bool is_64bit = ptype == GL_INT64_ARB || 824 ptype == GL_UNSIGNED_INT64_ARB; 825 if (!ctx->Extensions.ARB_query_buffer_object) { 826 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func); 827 return; 828 } 829 if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) { 830 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func); 831 return; 832 } 833 834 if (offset < 0) { 835 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func); 836 return; 837 } 838 839 switch (pname) { 840 case GL_QUERY_RESULT: 841 case GL_QUERY_RESULT_NO_WAIT: 842 case GL_QUERY_RESULT_AVAILABLE: 843 case GL_QUERY_TARGET: 844 ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype); 845 return; 846 } 847 848 /* fall through to get error below */ 849 } 850 851 switch (pname) { 852 case GL_QUERY_RESULT: 853 if (!q->Ready) 854 ctx->Driver.WaitQuery(ctx, q); 855 value = q->Result; 856 break; 857 case GL_QUERY_RESULT_NO_WAIT: 858 if (!ctx->Extensions.ARB_query_buffer_object) 859 goto invalid_enum; 860 ctx->Driver.CheckQuery(ctx, q); 861 if (!q->Ready) 862 return; 863 value = q->Result; 864 break; 865 case GL_QUERY_RESULT_AVAILABLE: 866 if (!q->Ready) 867 ctx->Driver.CheckQuery(ctx, q); 868 value = q->Ready; 869 break; 870 case GL_QUERY_TARGET: 871 value = q->Target; 872 break; 873 default: 874invalid_enum: 875 _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)", 876 func, _mesa_enum_to_string(pname)); 877 return; 878 } 879 880 switch (ptype) { 881 case GL_INT: { 882 GLint *param = (GLint *)offset; 883 if (value > 0x7fffffff) 884 *param = 0x7fffffff; 885 else 886 *param = value; 887 break; 888 } 889 case GL_UNSIGNED_INT: { 890 GLuint *param = (GLuint *)offset; 891 if (value > 0xffffffff) 892 *param = 0xffffffff; 893 else 894 *param = value; 895 break; 896 } 897 case GL_INT64_ARB: 898 case GL_UNSIGNED_INT64_ARB: { 899 GLuint64EXT *param = (GLuint64EXT *)offset; 900 *param = value; 901 break; 902 } 903 default: 904 unreachable("unexpected ptype"); 905 } 906} 907 908void GLAPIENTRY 909_mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params) 910{ 911 GET_CURRENT_CONTEXT(ctx); 912 913 get_query_object(ctx, "glGetQueryObjectiv", 914 id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params); 915} 916 917 918void GLAPIENTRY 919_mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) 920{ 921 GET_CURRENT_CONTEXT(ctx); 922 923 get_query_object(ctx, "glGetQueryObjectuiv", 924 id, pname, GL_UNSIGNED_INT, 925 ctx->QueryBuffer, (intptr_t)params); 926} 927 928 929/** 930 * New with GL_EXT_timer_query 931 */ 932void GLAPIENTRY 933_mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params) 934{ 935 GET_CURRENT_CONTEXT(ctx); 936 937 get_query_object(ctx, "glGetQueryObjecti64v", 938 id, pname, GL_INT64_ARB, 939 ctx->QueryBuffer, (intptr_t)params); 940} 941 942 943/** 944 * New with GL_EXT_timer_query 945 */ 946void GLAPIENTRY 947_mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params) 948{ 949 GET_CURRENT_CONTEXT(ctx); 950 951 get_query_object(ctx, "glGetQueryObjectui64v", 952 id, pname, GL_UNSIGNED_INT64_ARB, 953 ctx->QueryBuffer, (intptr_t)params); 954} 955 956/** 957 * New with GL_ARB_query_buffer_object 958 */ 959void GLAPIENTRY 960_mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname, 961 GLintptr offset) 962{ 963 struct gl_buffer_object *buf; 964 GET_CURRENT_CONTEXT(ctx); 965 966 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv"); 967 if (!buf) 968 return; 969 970 get_query_object(ctx, "glGetQueryBufferObjectiv", 971 id, pname, GL_INT, buf, offset); 972} 973 974 975void GLAPIENTRY 976_mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname, 977 GLintptr offset) 978{ 979 struct gl_buffer_object *buf; 980 GET_CURRENT_CONTEXT(ctx); 981 982 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv"); 983 if (!buf) 984 return; 985 986 get_query_object(ctx, "glGetQueryBufferObjectuiv", 987 id, pname, GL_UNSIGNED_INT, buf, offset); 988} 989 990 991void GLAPIENTRY 992_mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname, 993 GLintptr offset) 994{ 995 struct gl_buffer_object *buf; 996 GET_CURRENT_CONTEXT(ctx); 997 998 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v"); 999 if (!buf) 1000 return; 1001 1002 get_query_object(ctx, "glGetQueryBufferObjecti64v", 1003 id, pname, GL_INT64_ARB, buf, offset); 1004} 1005 1006 1007void GLAPIENTRY 1008_mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname, 1009 GLintptr offset) 1010{ 1011 struct gl_buffer_object *buf; 1012 GET_CURRENT_CONTEXT(ctx); 1013 1014 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v"); 1015 if (!buf) 1016 return; 1017 1018 get_query_object(ctx, "glGetQueryBufferObjectui64v", 1019 id, pname, GL_UNSIGNED_INT64_ARB, buf, offset); 1020} 1021 1022 1023/** 1024 * Allocate/init the context state related to query objects. 1025 */ 1026void 1027_mesa_init_queryobj(struct gl_context *ctx) 1028{ 1029 ctx->Query.QueryObjects = _mesa_NewHashTable(); 1030 ctx->Query.CurrentOcclusionObject = NULL; 1031 1032 ctx->Const.QueryCounterBits.SamplesPassed = 64; 1033 ctx->Const.QueryCounterBits.TimeElapsed = 64; 1034 ctx->Const.QueryCounterBits.Timestamp = 64; 1035 ctx->Const.QueryCounterBits.PrimitivesGenerated = 64; 1036 ctx->Const.QueryCounterBits.PrimitivesWritten = 64; 1037 1038 ctx->Const.QueryCounterBits.VerticesSubmitted = 64; 1039 ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64; 1040 ctx->Const.QueryCounterBits.VsInvocations = 64; 1041 ctx->Const.QueryCounterBits.TessPatches = 64; 1042 ctx->Const.QueryCounterBits.TessInvocations = 64; 1043 ctx->Const.QueryCounterBits.GsInvocations = 64; 1044 ctx->Const.QueryCounterBits.GsPrimitives = 64; 1045 ctx->Const.QueryCounterBits.FsInvocations = 64; 1046 ctx->Const.QueryCounterBits.ComputeInvocations = 64; 1047 ctx->Const.QueryCounterBits.ClInPrimitives = 64; 1048 ctx->Const.QueryCounterBits.ClOutPrimitives = 64; 1049} 1050 1051 1052/** 1053 * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). 1054 */ 1055static void 1056delete_queryobj_cb(GLuint id, void *data, void *userData) 1057{ 1058 struct gl_query_object *q= (struct gl_query_object *) data; 1059 struct gl_context *ctx = (struct gl_context *)userData; 1060 ctx->Driver.DeleteQuery(ctx, q); 1061} 1062 1063 1064/** 1065 * Free the context state related to query objects. 1066 */ 1067void 1068_mesa_free_queryobj_data(struct gl_context *ctx) 1069{ 1070 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); 1071 _mesa_DeleteHashTable(ctx->Query.QueryObjects); 1072} 1073