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