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; 150 assert(which < MAX_PIPELINE_STATISTICS); 151 152 if (!_mesa_has_ARB_pipeline_statistics_query(ctx)) 153 return NULL; 154 155 return &ctx->Query.pipeline_stats[which]; 156} 157 158/** 159 * Return pointer to the query object binding point for the given target and 160 * index. 161 * \return NULL if invalid target, else the address of binding point 162 */ 163static struct gl_query_object ** 164get_query_binding_point(struct gl_context *ctx, GLenum target, GLuint index) 165{ 166 switch (target) { 167 case GL_SAMPLES_PASSED: 168 if (_mesa_has_ARB_occlusion_query(ctx) || 169 _mesa_has_ARB_occlusion_query2(ctx)) 170 return &ctx->Query.CurrentOcclusionObject; 171 else 172 return NULL; 173 case GL_ANY_SAMPLES_PASSED: 174 if (_mesa_has_ARB_occlusion_query2(ctx) || 175 _mesa_has_EXT_occlusion_query_boolean(ctx)) 176 return &ctx->Query.CurrentOcclusionObject; 177 else 178 return NULL; 179 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 180 if (_mesa_has_ARB_ES3_compatibility(ctx) || 181 _mesa_has_EXT_occlusion_query_boolean(ctx)) 182 return &ctx->Query.CurrentOcclusionObject; 183 else 184 return NULL; 185 case GL_TIME_ELAPSED: 186 if (_mesa_has_EXT_timer_query(ctx) || 187 _mesa_has_EXT_disjoint_timer_query(ctx)) 188 return &ctx->Query.CurrentTimerObject; 189 else 190 return NULL; 191 case GL_PRIMITIVES_GENERATED: 192 if (_mesa_has_EXT_transform_feedback(ctx) || 193 _mesa_has_EXT_tessellation_shader(ctx) || 194 _mesa_has_OES_geometry_shader(ctx)) 195 return &ctx->Query.PrimitivesGenerated[index]; 196 else 197 return NULL; 198 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 199 if (_mesa_has_EXT_transform_feedback(ctx) || _mesa_is_gles3(ctx)) 200 return &ctx->Query.PrimitivesWritten[index]; 201 else 202 return NULL; 203 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW: 204 if (_mesa_has_ARB_transform_feedback_overflow_query(ctx)) 205 return &ctx->Query.TransformFeedbackOverflow[index]; 206 else 207 return NULL; 208 case GL_TRANSFORM_FEEDBACK_OVERFLOW: 209 if (_mesa_has_ARB_transform_feedback_overflow_query(ctx)) 210 return &ctx->Query.TransformFeedbackOverflowAny; 211 else 212 return NULL; 213 214 case GL_VERTICES_SUBMITTED: 215 case GL_PRIMITIVES_SUBMITTED: 216 case GL_VERTEX_SHADER_INVOCATIONS: 217 case GL_FRAGMENT_SHADER_INVOCATIONS: 218 case GL_CLIPPING_INPUT_PRIMITIVES: 219 case GL_CLIPPING_OUTPUT_PRIMITIVES: 220 return get_pipe_stats_binding_point(ctx, target); 221 222 case GL_GEOMETRY_SHADER_INVOCATIONS: 223 /* GL_GEOMETRY_SHADER_INVOCATIONS is defined in a non-sequential order */ 224 target = GL_VERTICES_SUBMITTED + MAX_PIPELINE_STATISTICS - 1; 225 /* fallthrough */ 226 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED: 227 if (_mesa_has_geometry_shaders(ctx)) 228 return get_pipe_stats_binding_point(ctx, target); 229 else 230 return NULL; 231 232 case GL_TESS_CONTROL_SHADER_PATCHES: 233 case GL_TESS_EVALUATION_SHADER_INVOCATIONS: 234 if (_mesa_has_tessellation(ctx)) 235 return get_pipe_stats_binding_point(ctx, target); 236 else 237 return NULL; 238 239 case GL_COMPUTE_SHADER_INVOCATIONS: 240 if (_mesa_has_compute_shaders(ctx)) 241 return get_pipe_stats_binding_point(ctx, target); 242 else 243 return NULL; 244 245 default: 246 return NULL; 247 } 248} 249 250/** 251 * Create $n query objects and store them in *ids. Make them of type $target 252 * if dsa is set. Called from _mesa_GenQueries() and _mesa_CreateQueries(). 253 */ 254static void 255create_queries(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *ids, 256 bool dsa) 257{ 258 const char *func = dsa ? "glGenQueries" : "glCreateQueries"; 259 GLuint first; 260 261 if (MESA_VERBOSE & VERBOSE_API) 262 _mesa_debug(ctx, "%s(%d)\n", func, n); 263 264 if (n < 0) { 265 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); 266 return; 267 } 268 269 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); 270 if (first) { 271 GLsizei i; 272 for (i = 0; i < n; i++) { 273 struct gl_query_object *q 274 = ctx->Driver.NewQueryObject(ctx, first + i); 275 if (!q) { 276 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 277 return; 278 } else if (dsa) { 279 /* Do the equivalent of binding the buffer with a target */ 280 q->Target = target; 281 q->EverBound = GL_TRUE; 282 } 283 ids[i] = first + i; 284 _mesa_HashInsertLocked(ctx->Query.QueryObjects, first + i, q); 285 } 286 } 287} 288 289void GLAPIENTRY 290_mesa_GenQueries(GLsizei n, GLuint *ids) 291{ 292 GET_CURRENT_CONTEXT(ctx); 293 create_queries(ctx, 0, n, ids, false); 294} 295 296void GLAPIENTRY 297_mesa_CreateQueries(GLenum target, GLsizei n, GLuint *ids) 298{ 299 GET_CURRENT_CONTEXT(ctx); 300 301 switch (target) { 302 case GL_SAMPLES_PASSED: 303 case GL_ANY_SAMPLES_PASSED: 304 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 305 case GL_TIME_ELAPSED: 306 case GL_TIMESTAMP: 307 case GL_PRIMITIVES_GENERATED: 308 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 309 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW: 310 case GL_TRANSFORM_FEEDBACK_OVERFLOW: 311 break; 312 default: 313 _mesa_error(ctx, GL_INVALID_ENUM, "glCreateQueries(invalid target = %s)", 314 _mesa_enum_to_string(target)); 315 return; 316 } 317 318 create_queries(ctx, target, n, ids, true); 319} 320 321 322void GLAPIENTRY 323_mesa_DeleteQueries(GLsizei n, const GLuint *ids) 324{ 325 GLint i; 326 GET_CURRENT_CONTEXT(ctx); 327 FLUSH_VERTICES(ctx, 0); 328 329 if (MESA_VERBOSE & VERBOSE_API) 330 _mesa_debug(ctx, "glDeleteQueries(%d)\n", n); 331 332 if (n < 0) { 333 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); 334 return; 335 } 336 337 for (i = 0; i < n; i++) { 338 if (ids[i] > 0) { 339 struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]); 340 if (q) { 341 if (q->Active) { 342 struct gl_query_object **bindpt; 343 bindpt = get_query_binding_point(ctx, q->Target, q->Stream); 344 assert(bindpt); /* Should be non-null for active q. */ 345 if (bindpt) { 346 *bindpt = NULL; 347 } 348 q->Active = GL_FALSE; 349 ctx->Driver.EndQuery(ctx, q); 350 } 351 _mesa_HashRemoveLocked(ctx->Query.QueryObjects, ids[i]); 352 ctx->Driver.DeleteQuery(ctx, q); 353 } 354 } 355 } 356} 357 358 359GLboolean GLAPIENTRY 360_mesa_IsQuery(GLuint id) 361{ 362 struct gl_query_object *q; 363 364 GET_CURRENT_CONTEXT(ctx); 365 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 366 367 if (MESA_VERBOSE & VERBOSE_API) 368 _mesa_debug(ctx, "glIsQuery(%u)\n", id); 369 370 if (id == 0) 371 return GL_FALSE; 372 373 q = _mesa_lookup_query_object(ctx, id); 374 if (q == NULL) 375 return GL_FALSE; 376 377 return q->EverBound; 378} 379 380static GLboolean 381query_error_check_index(struct gl_context *ctx, GLenum target, GLuint index) 382{ 383 switch (target) { 384 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 385 case GL_PRIMITIVES_GENERATED: 386 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW: 387 if (index >= ctx->Const.MaxVertexStreams) { 388 _mesa_error(ctx, GL_INVALID_VALUE, 389 "glBeginQueryIndexed(index>=MaxVertexStreams)"); 390 return GL_FALSE; 391 } 392 break; 393 default: 394 if (index > 0) { 395 _mesa_error(ctx, GL_INVALID_VALUE, "glBeginQueryIndexed(index>0)"); 396 return GL_FALSE; 397 } 398 } 399 return GL_TRUE; 400} 401 402void GLAPIENTRY 403_mesa_BeginQueryIndexed(GLenum target, GLuint index, GLuint id) 404{ 405 struct gl_query_object *q, **bindpt; 406 GET_CURRENT_CONTEXT(ctx); 407 408 if (MESA_VERBOSE & VERBOSE_API) 409 _mesa_debug(ctx, "glBeginQueryIndexed(%s, %u, %u)\n", 410 _mesa_enum_to_string(target), index, id); 411 412 if (!query_error_check_index(ctx, target, index)) 413 return; 414 415 FLUSH_VERTICES(ctx, 0); 416 417 bindpt = get_query_binding_point(ctx, target, index); 418 if (!bindpt) { 419 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQuery{Indexed}(target)"); 420 return; 421 } 422 423 /* From the GL_ARB_occlusion_query spec: 424 * 425 * "If BeginQueryARB is called while another query is already in 426 * progress with the same target, an INVALID_OPERATION error is 427 * generated." 428 */ 429 if (*bindpt) { 430 _mesa_error(ctx, GL_INVALID_OPERATION, 431 "glBeginQuery{Indexed}(target=%s is active)", 432 _mesa_enum_to_string(target)); 433 return; 434 } 435 436 if (id == 0) { 437 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQuery{Indexed}(id==0)"); 438 return; 439 } 440 441 q = _mesa_lookup_query_object(ctx, id); 442 if (!q) { 443 if (ctx->API != API_OPENGL_COMPAT) { 444 _mesa_error(ctx, GL_INVALID_OPERATION, 445 "glBeginQuery{Indexed}(non-gen name)"); 446 return; 447 } else { 448 /* create new object */ 449 q = ctx->Driver.NewQueryObject(ctx, id); 450 if (!q) { 451 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQuery{Indexed}"); 452 return; 453 } 454 _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q); 455 } 456 } 457 else { 458 /* pre-existing object */ 459 if (q->Active) { 460 _mesa_error(ctx, GL_INVALID_OPERATION, 461 "glBeginQuery{Indexed}(query already active)"); 462 return; 463 } 464 465 /* Section 2.14 Asynchronous Queries, page 84 of the OpenGL ES 3.0.4 466 * spec states: 467 * 468 * "BeginQuery generates an INVALID_OPERATION error if any of the 469 * following conditions hold: [...] id is the name of an 470 * existing query object whose type does not match target; [...] 471 * 472 * Similar wording exists in the OpenGL 4.5 spec, section 4.2. QUERY 473 * OBJECTS AND ASYNCHRONOUS QUERIES, page 43. 474 */ 475 if (q->EverBound && q->Target != target) { 476 _mesa_error(ctx, GL_INVALID_OPERATION, 477 "glBeginQuery{Indexed}(target mismatch)"); 478 return; 479 } 480 } 481 482 /* This possibly changes the target of a buffer allocated by 483 * CreateQueries. Issue 39) in the ARB_direct_state_access extension states 484 * the following: 485 * 486 * "CreateQueries adds a <target>, so strictly speaking the <target> 487 * command isn't needed for BeginQuery/EndQuery, but in the end, this also 488 * isn't a selector, so we decided not to change it." 489 * 490 * Updating the target of the query object should be acceptable, so let's 491 * do that. 492 */ 493 494 q->Target = target; 495 q->Active = GL_TRUE; 496 q->Result = 0; 497 q->Ready = GL_FALSE; 498 q->EverBound = GL_TRUE; 499 q->Stream = index; 500 501 /* XXX should probably refcount query objects */ 502 *bindpt = q; 503 504 ctx->Driver.BeginQuery(ctx, q); 505} 506 507 508void GLAPIENTRY 509_mesa_EndQueryIndexed(GLenum target, GLuint index) 510{ 511 struct gl_query_object *q, **bindpt; 512 GET_CURRENT_CONTEXT(ctx); 513 514 if (MESA_VERBOSE & VERBOSE_API) 515 _mesa_debug(ctx, "glEndQueryIndexed(%s, %u)\n", 516 _mesa_enum_to_string(target), index); 517 518 if (!query_error_check_index(ctx, target, index)) 519 return; 520 521 FLUSH_VERTICES(ctx, 0); 522 523 bindpt = get_query_binding_point(ctx, target, index); 524 if (!bindpt) { 525 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQuery{Indexed}(target)"); 526 return; 527 } 528 529 /* XXX should probably refcount query objects */ 530 q = *bindpt; 531 532 /* Check for GL_ANY_SAMPLES_PASSED vs GL_SAMPLES_PASSED. */ 533 if (q && q->Target != target) { 534 _mesa_error(ctx, GL_INVALID_OPERATION, 535 "glEndQuery(target=%s with active query of target %s)", 536 _mesa_enum_to_string(target), 537 _mesa_enum_to_string(q->Target)); 538 return; 539 } 540 541 *bindpt = NULL; 542 543 if (!q || !q->Active) { 544 _mesa_error(ctx, GL_INVALID_OPERATION, 545 "glEndQuery{Indexed}(no matching glBeginQuery{Indexed})"); 546 return; 547 } 548 549 q->Active = GL_FALSE; 550 ctx->Driver.EndQuery(ctx, q); 551} 552 553void GLAPIENTRY 554_mesa_BeginQuery(GLenum target, GLuint id) 555{ 556 _mesa_BeginQueryIndexed(target, 0, id); 557} 558 559void GLAPIENTRY 560_mesa_EndQuery(GLenum target) 561{ 562 _mesa_EndQueryIndexed(target, 0); 563} 564 565void GLAPIENTRY 566_mesa_QueryCounter(GLuint id, GLenum target) 567{ 568 struct gl_query_object *q; 569 GET_CURRENT_CONTEXT(ctx); 570 571 if (MESA_VERBOSE & VERBOSE_API) 572 _mesa_debug(ctx, "glQueryCounter(%u, %s)\n", id, 573 _mesa_enum_to_string(target)); 574 575 /* error checking */ 576 if (target != GL_TIMESTAMP) { 577 _mesa_error(ctx, GL_INVALID_ENUM, "glQueryCounter(target)"); 578 return; 579 } 580 581 if (id == 0) { 582 _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id==0)"); 583 return; 584 } 585 586 q = _mesa_lookup_query_object(ctx, id); 587 if (!q) { 588 /* XXX the Core profile should throw INVALID_OPERATION here */ 589 590 /* create new object */ 591 q = ctx->Driver.NewQueryObject(ctx, id); 592 if (!q) { 593 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glQueryCounter"); 594 return; 595 } 596 _mesa_HashInsertLocked(ctx->Query.QueryObjects, id, q); 597 } 598 else { 599 if (q->Target && q->Target != GL_TIMESTAMP) { 600 _mesa_error(ctx, GL_INVALID_OPERATION, 601 "glQueryCounter(id has an invalid target)"); 602 return; 603 } 604 } 605 606 if (q->Active) { 607 _mesa_error(ctx, GL_INVALID_OPERATION, "glQueryCounter(id is active)"); 608 return; 609 } 610 611 /* This possibly changes the target of a buffer allocated by 612 * CreateQueries. Issue 39) in the ARB_direct_state_access extension states 613 * the following: 614 * 615 * "CreateQueries adds a <target>, so strictly speaking the <target> 616 * command isn't needed for BeginQuery/EndQuery, but in the end, this also 617 * isn't a selector, so we decided not to change it." 618 * 619 * Updating the target of the query object should be acceptable, so let's 620 * do that. 621 */ 622 623 q->Target = target; 624 q->Result = 0; 625 q->Ready = GL_FALSE; 626 q->EverBound = GL_TRUE; 627 628 if (ctx->Driver.QueryCounter) { 629 ctx->Driver.QueryCounter(ctx, q); 630 } else { 631 /* QueryCounter is implemented using EndQuery without BeginQuery 632 * in drivers. This is actually Direct3D and Gallium convention. 633 */ 634 ctx->Driver.EndQuery(ctx, q); 635 } 636} 637 638 639void GLAPIENTRY 640_mesa_GetQueryIndexediv(GLenum target, GLuint index, GLenum pname, 641 GLint *params) 642{ 643 struct gl_query_object *q = NULL, **bindpt = NULL; 644 GET_CURRENT_CONTEXT(ctx); 645 646 if (MESA_VERBOSE & VERBOSE_API) 647 _mesa_debug(ctx, "glGetQueryIndexediv(%s, %u, %s)\n", 648 _mesa_enum_to_string(target), 649 index, 650 _mesa_enum_to_string(pname)); 651 652 if (!query_error_check_index(ctx, target, index)) 653 return; 654 655 /* From the GL_EXT_occlusion_query_boolean spec: 656 * 657 * "The error INVALID_ENUM is generated if GetQueryivEXT is called where 658 * <pname> is not CURRENT_QUERY_EXT." 659 * 660 * Same rule is present also in ES 3.2 spec. 661 */ 662 if (_mesa_is_gles(ctx) && pname != GL_CURRENT_QUERY) { 663 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivEXT(%s)", 664 _mesa_enum_to_string(pname)); 665 return; 666 } 667 668 if (target == GL_TIMESTAMP) { 669 if (!_mesa_has_ARB_timer_query(ctx) && 670 !_mesa_has_EXT_disjoint_timer_query(ctx)) { 671 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)"); 672 return; 673 } 674 } 675 else { 676 bindpt = get_query_binding_point(ctx, target, index); 677 if (!bindpt) { 678 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(target)"); 679 return; 680 } 681 682 q = *bindpt; 683 } 684 685 switch (pname) { 686 case GL_QUERY_COUNTER_BITS: 687 switch (target) { 688 case GL_SAMPLES_PASSED: 689 *params = ctx->Const.QueryCounterBits.SamplesPassed; 690 break; 691 case GL_ANY_SAMPLES_PASSED: 692 case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 693 /* The minimum value of this is 1 if it's nonzero, and the value 694 * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more 695 * bits. 696 */ 697 *params = 1; 698 break; 699 case GL_TIME_ELAPSED: 700 *params = ctx->Const.QueryCounterBits.TimeElapsed; 701 break; 702 case GL_TIMESTAMP: 703 *params = ctx->Const.QueryCounterBits.Timestamp; 704 break; 705 case GL_PRIMITIVES_GENERATED: 706 *params = ctx->Const.QueryCounterBits.PrimitivesGenerated; 707 break; 708 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 709 *params = ctx->Const.QueryCounterBits.PrimitivesWritten; 710 break; 711 case GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW: 712 case GL_TRANSFORM_FEEDBACK_OVERFLOW: 713 /* The minimum value of this is 1 if it's nonzero, and the value 714 * is only ever GL_TRUE or GL_FALSE, so no sense in reporting more 715 * bits. 716 */ 717 *params = 1; 718 break; 719 case GL_VERTICES_SUBMITTED: 720 *params = ctx->Const.QueryCounterBits.VerticesSubmitted; 721 break; 722 case GL_PRIMITIVES_SUBMITTED: 723 *params = ctx->Const.QueryCounterBits.PrimitivesSubmitted; 724 break; 725 case GL_VERTEX_SHADER_INVOCATIONS: 726 *params = ctx->Const.QueryCounterBits.VsInvocations; 727 break; 728 case GL_TESS_CONTROL_SHADER_PATCHES: 729 *params = ctx->Const.QueryCounterBits.TessPatches; 730 break; 731 case GL_TESS_EVALUATION_SHADER_INVOCATIONS: 732 *params = ctx->Const.QueryCounterBits.TessInvocations; 733 break; 734 case GL_GEOMETRY_SHADER_INVOCATIONS: 735 *params = ctx->Const.QueryCounterBits.GsInvocations; 736 break; 737 case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED: 738 *params = ctx->Const.QueryCounterBits.GsPrimitives; 739 break; 740 case GL_FRAGMENT_SHADER_INVOCATIONS: 741 *params = ctx->Const.QueryCounterBits.FsInvocations; 742 break; 743 case GL_COMPUTE_SHADER_INVOCATIONS: 744 *params = ctx->Const.QueryCounterBits.ComputeInvocations; 745 break; 746 case GL_CLIPPING_INPUT_PRIMITIVES: 747 *params = ctx->Const.QueryCounterBits.ClInPrimitives; 748 break; 749 case GL_CLIPPING_OUTPUT_PRIMITIVES: 750 *params = ctx->Const.QueryCounterBits.ClOutPrimitives; 751 break; 752 default: 753 _mesa_problem(ctx, 754 "Unknown target in glGetQueryIndexediv(target = %s)", 755 _mesa_enum_to_string(target)); 756 *params = 0; 757 break; 758 } 759 break; 760 case GL_CURRENT_QUERY: 761 *params = (q && q->Target == target) ? q->Id : 0; 762 break; 763 default: 764 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQuery{Indexed}iv(pname)"); 765 return; 766 } 767} 768 769void GLAPIENTRY 770_mesa_GetQueryiv(GLenum target, GLenum pname, GLint *params) 771{ 772 _mesa_GetQueryIndexediv(target, 0, pname, params); 773} 774 775static void 776get_query_object(struct gl_context *ctx, const char *func, 777 GLuint id, GLenum pname, GLenum ptype, 778 struct gl_buffer_object *buf, intptr_t offset) 779{ 780 struct gl_query_object *q = NULL; 781 uint64_t value; 782 783 if (MESA_VERBOSE & VERBOSE_API) 784 _mesa_debug(ctx, "%s(%u, %s)\n", func, id, 785 _mesa_enum_to_string(pname)); 786 787 if (id) 788 q = _mesa_lookup_query_object(ctx, id); 789 790 if (!q || q->Active || !q->EverBound) { 791 _mesa_error(ctx, GL_INVALID_OPERATION, 792 "%s(id=%d is invalid or active)", func, id); 793 return; 794 } 795 796 /* From GL_EXT_occlusion_query_boolean spec: 797 * 798 * "Accepted by the <pname> parameter of GetQueryObjectivEXT and 799 * GetQueryObjectuivEXT: 800 * 801 * QUERY_RESULT_EXT 0x8866 802 * QUERY_RESULT_AVAILABLE_EXT 0x8867" 803 * 804 * Same rule is present also in ES 3.2 spec. 805 */ 806 if (_mesa_is_gles(ctx) && 807 (pname != GL_QUERY_RESULT && pname != GL_QUERY_RESULT_AVAILABLE)) { 808 _mesa_error(ctx, GL_INVALID_ENUM, "%s(%s)", func, 809 _mesa_enum_to_string(pname)); 810 return; 811 } 812 813 if (buf && buf != ctx->Shared->NullBufferObj) { 814 bool is_64bit = ptype == GL_INT64_ARB || 815 ptype == GL_UNSIGNED_INT64_ARB; 816 if (!_mesa_has_ARB_query_buffer_object(ctx)) { 817 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(not supported)", func); 818 return; 819 } 820 if (buf->Size < offset + 4 * (is_64bit ? 2 : 1)) { 821 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds)", func); 822 return; 823 } 824 825 if (offset < 0) { 826 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset is negative)", func); 827 return; 828 } 829 830 switch (pname) { 831 case GL_QUERY_RESULT: 832 case GL_QUERY_RESULT_NO_WAIT: 833 case GL_QUERY_RESULT_AVAILABLE: 834 case GL_QUERY_TARGET: 835 ctx->Driver.StoreQueryResult(ctx, q, buf, offset, pname, ptype); 836 return; 837 } 838 839 /* fall through to get error below */ 840 } 841 842 switch (pname) { 843 case GL_QUERY_RESULT: 844 if (!q->Ready) 845 ctx->Driver.WaitQuery(ctx, q); 846 value = q->Result; 847 break; 848 case GL_QUERY_RESULT_NO_WAIT: 849 if (!_mesa_has_ARB_query_buffer_object(ctx)) 850 goto invalid_enum; 851 ctx->Driver.CheckQuery(ctx, q); 852 if (!q->Ready) 853 return; 854 value = q->Result; 855 break; 856 case GL_QUERY_RESULT_AVAILABLE: 857 if (!q->Ready) 858 ctx->Driver.CheckQuery(ctx, q); 859 value = q->Ready; 860 break; 861 case GL_QUERY_TARGET: 862 value = q->Target; 863 break; 864 default: 865invalid_enum: 866 _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=%s)", 867 func, _mesa_enum_to_string(pname)); 868 return; 869 } 870 871 switch (ptype) { 872 case GL_INT: { 873 GLint *param = (GLint *)offset; 874 if (value > 0x7fffffff) 875 *param = 0x7fffffff; 876 else 877 *param = value; 878 break; 879 } 880 case GL_UNSIGNED_INT: { 881 GLuint *param = (GLuint *)offset; 882 if (value > 0xffffffff) 883 *param = 0xffffffff; 884 else 885 *param = value; 886 break; 887 } 888 case GL_INT64_ARB: 889 case GL_UNSIGNED_INT64_ARB: { 890 GLuint64EXT *param = (GLuint64EXT *)offset; 891 *param = value; 892 break; 893 } 894 default: 895 unreachable("unexpected ptype"); 896 } 897} 898 899void GLAPIENTRY 900_mesa_GetQueryObjectiv(GLuint id, GLenum pname, GLint *params) 901{ 902 GET_CURRENT_CONTEXT(ctx); 903 904 get_query_object(ctx, "glGetQueryObjectiv", 905 id, pname, GL_INT, ctx->QueryBuffer, (intptr_t)params); 906} 907 908 909void GLAPIENTRY 910_mesa_GetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) 911{ 912 GET_CURRENT_CONTEXT(ctx); 913 914 get_query_object(ctx, "glGetQueryObjectuiv", 915 id, pname, GL_UNSIGNED_INT, 916 ctx->QueryBuffer, (intptr_t)params); 917} 918 919 920/** 921 * New with GL_EXT_timer_query 922 */ 923void GLAPIENTRY 924_mesa_GetQueryObjecti64v(GLuint id, GLenum pname, GLint64EXT *params) 925{ 926 GET_CURRENT_CONTEXT(ctx); 927 928 get_query_object(ctx, "glGetQueryObjecti64v", 929 id, pname, GL_INT64_ARB, 930 ctx->QueryBuffer, (intptr_t)params); 931} 932 933 934/** 935 * New with GL_EXT_timer_query 936 */ 937void GLAPIENTRY 938_mesa_GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params) 939{ 940 GET_CURRENT_CONTEXT(ctx); 941 942 get_query_object(ctx, "glGetQueryObjectui64v", 943 id, pname, GL_UNSIGNED_INT64_ARB, 944 ctx->QueryBuffer, (intptr_t)params); 945} 946 947/** 948 * New with GL_ARB_query_buffer_object 949 */ 950void GLAPIENTRY 951_mesa_GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname, 952 GLintptr offset) 953{ 954 struct gl_buffer_object *buf; 955 GET_CURRENT_CONTEXT(ctx); 956 957 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectiv"); 958 if (!buf) 959 return; 960 961 get_query_object(ctx, "glGetQueryBufferObjectiv", 962 id, pname, GL_INT, buf, offset); 963} 964 965 966void GLAPIENTRY 967_mesa_GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname, 968 GLintptr offset) 969{ 970 struct gl_buffer_object *buf; 971 GET_CURRENT_CONTEXT(ctx); 972 973 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectuiv"); 974 if (!buf) 975 return; 976 977 get_query_object(ctx, "glGetQueryBufferObjectuiv", 978 id, pname, GL_UNSIGNED_INT, buf, offset); 979} 980 981 982void GLAPIENTRY 983_mesa_GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname, 984 GLintptr offset) 985{ 986 struct gl_buffer_object *buf; 987 GET_CURRENT_CONTEXT(ctx); 988 989 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjecti64v"); 990 if (!buf) 991 return; 992 993 get_query_object(ctx, "glGetQueryBufferObjecti64v", 994 id, pname, GL_INT64_ARB, buf, offset); 995} 996 997 998void GLAPIENTRY 999_mesa_GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname, 1000 GLintptr offset) 1001{ 1002 struct gl_buffer_object *buf; 1003 GET_CURRENT_CONTEXT(ctx); 1004 1005 buf = _mesa_lookup_bufferobj_err(ctx, buffer, "glGetQueryBufferObjectui64v"); 1006 if (!buf) 1007 return; 1008 1009 get_query_object(ctx, "glGetQueryBufferObjectui64v", 1010 id, pname, GL_UNSIGNED_INT64_ARB, buf, offset); 1011} 1012 1013 1014/** 1015 * Allocate/init the context state related to query objects. 1016 */ 1017void 1018_mesa_init_queryobj(struct gl_context *ctx) 1019{ 1020 ctx->Query.QueryObjects = _mesa_NewHashTable(); 1021 ctx->Query.CurrentOcclusionObject = NULL; 1022 1023 ctx->Const.QueryCounterBits.SamplesPassed = 64; 1024 ctx->Const.QueryCounterBits.TimeElapsed = 64; 1025 ctx->Const.QueryCounterBits.Timestamp = 64; 1026 ctx->Const.QueryCounterBits.PrimitivesGenerated = 64; 1027 ctx->Const.QueryCounterBits.PrimitivesWritten = 64; 1028 1029 ctx->Const.QueryCounterBits.VerticesSubmitted = 64; 1030 ctx->Const.QueryCounterBits.PrimitivesSubmitted = 64; 1031 ctx->Const.QueryCounterBits.VsInvocations = 64; 1032 ctx->Const.QueryCounterBits.TessPatches = 64; 1033 ctx->Const.QueryCounterBits.TessInvocations = 64; 1034 ctx->Const.QueryCounterBits.GsInvocations = 64; 1035 ctx->Const.QueryCounterBits.GsPrimitives = 64; 1036 ctx->Const.QueryCounterBits.FsInvocations = 64; 1037 ctx->Const.QueryCounterBits.ComputeInvocations = 64; 1038 ctx->Const.QueryCounterBits.ClInPrimitives = 64; 1039 ctx->Const.QueryCounterBits.ClOutPrimitives = 64; 1040} 1041 1042 1043/** 1044 * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). 1045 */ 1046static void 1047delete_queryobj_cb(GLuint id, void *data, void *userData) 1048{ 1049 struct gl_query_object *q= (struct gl_query_object *) data; 1050 struct gl_context *ctx = (struct gl_context *)userData; 1051 ctx->Driver.DeleteQuery(ctx, q); 1052} 1053 1054 1055/** 1056 * Free the context state related to query objects. 1057 */ 1058void 1059_mesa_free_queryobj_data(struct gl_context *ctx) 1060{ 1061 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); 1062 _mesa_DeleteHashTable(ctx->Query.QueryObjects); 1063} 1064