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