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