1 /* 2 * Copyright 2012 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 /** 25 * \file performance_monitor.c 26 * Core Mesa support for the AMD_performance_monitor extension. 27 * 28 * In order to implement this extension, start by defining two enums: 29 * one for Groups, and one for Counters. These will be used as indexes into 30 * arrays, so they should start at 0 and increment from there. 31 * 32 * Counter IDs need to be globally unique. That is, you can't have counter 7 33 * in group A and counter 7 in group B. A global enum of all available 34 * counters is a convenient way to guarantee this. 35 */ 36 37 #include <stdbool.h> 38 #include "glheader.h" 39 #include "context.h" 40 #include "enums.h" 41 #include "hash.h" 42 #include "macros.h" 43 #include "mtypes.h" 44 #include "performance_monitor.h" 45 #include "util/bitset.h" 46 #include "util/ralloc.h" 47 48 void 49 _mesa_init_performance_monitors(struct gl_context *ctx) 50 { 51 ctx->PerfMonitor.Monitors = _mesa_NewHashTable(); 52 ctx->PerfMonitor.NumGroups = 0; 53 ctx->PerfMonitor.Groups = NULL; 54 } 55 56 static inline void 57 init_groups(struct gl_context *ctx) 58 { 59 if (unlikely(!ctx->PerfMonitor.Groups)) 60 ctx->Driver.InitPerfMonitorGroups(ctx); 61 } 62 63 static struct gl_perf_monitor_object * 64 new_performance_monitor(struct gl_context *ctx, GLuint index) 65 { 66 unsigned i; 67 struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx); 68 69 if (m == NULL) 70 return NULL; 71 72 m->Name = index; 73 74 m->Active = false; 75 76 m->ActiveGroups = 77 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups); 78 79 m->ActiveCounters = 80 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups); 81 82 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL) 83 goto fail; 84 85 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) { 86 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i]; 87 88 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD, 89 BITSET_WORDS(g->NumCounters)); 90 if (m->ActiveCounters[i] == NULL) 91 goto fail; 92 } 93 94 return m; 95 96 fail: 97 ralloc_free(m->ActiveGroups); 98 ralloc_free(m->ActiveCounters); 99 ctx->Driver.DeletePerfMonitor(ctx, m); 100 return NULL; 101 } 102 103 static void 104 free_performance_monitor(GLuint key, void *data, void *user) 105 { 106 struct gl_perf_monitor_object *m = data; 107 struct gl_context *ctx = user; 108 109 ralloc_free(m->ActiveGroups); 110 ralloc_free(m->ActiveCounters); 111 ctx->Driver.DeletePerfMonitor(ctx, m); 112 } 113 114 void 115 _mesa_free_performance_monitors(struct gl_context *ctx) 116 { 117 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors, 118 free_performance_monitor, ctx); 119 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors); 120 } 121 122 static inline struct gl_perf_monitor_object * 123 lookup_monitor(struct gl_context *ctx, GLuint id) 124 { 125 return (struct gl_perf_monitor_object *) 126 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id); 127 } 128 129 static inline const struct gl_perf_monitor_group * 130 get_group(const struct gl_context *ctx, GLuint id) 131 { 132 if (id >= ctx->PerfMonitor.NumGroups) 133 return NULL; 134 135 return &ctx->PerfMonitor.Groups[id]; 136 } 137 138 static inline const struct gl_perf_monitor_counter * 139 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id) 140 { 141 if (id >= group_obj->NumCounters) 142 return NULL; 143 144 return &group_obj->Counters[id]; 145 } 146 147 /* For INTEL_performance_query, query id 0 is reserved to be invalid. We use 148 * index to Groups array + 1 as the query id. Same applies to counter id. 149 */ 150 static inline GLuint 151 queryid_to_index(GLuint queryid) 152 { 153 return queryid - 1; 154 } 155 156 static inline GLuint 157 index_to_queryid(GLuint index) 158 { 159 return index + 1; 160 } 161 162 static inline bool 163 queryid_valid(const struct gl_context *ctx, GLuint queryid) 164 { 165 return get_group(ctx, queryid_to_index(queryid)) != NULL; 166 } 167 168 static inline GLuint 169 counterid_to_index(GLuint counterid) 170 { 171 return counterid - 1; 172 } 173 174 /*****************************************************************************/ 175 176 void GLAPIENTRY 177 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize, 178 GLuint *groups) 179 { 180 GET_CURRENT_CONTEXT(ctx); 181 init_groups(ctx); 182 183 if (numGroups != NULL) 184 *numGroups = ctx->PerfMonitor.NumGroups; 185 186 if (groupsSize > 0 && groups != NULL) { 187 unsigned i; 188 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups); 189 190 /* We just use the index in the Groups array as the ID. */ 191 for (i = 0; i < n; i++) 192 groups[i] = i; 193 } 194 } 195 196 void GLAPIENTRY 197 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters, 198 GLint *maxActiveCounters, 199 GLsizei countersSize, GLuint *counters) 200 { 201 GET_CURRENT_CONTEXT(ctx); 202 const struct gl_perf_monitor_group *group_obj; 203 204 init_groups(ctx); 205 206 group_obj = get_group(ctx, group); 207 if (group_obj == NULL) { 208 _mesa_error(ctx, GL_INVALID_VALUE, 209 "glGetPerfMonitorCountersAMD(invalid group)"); 210 return; 211 } 212 213 if (maxActiveCounters != NULL) 214 *maxActiveCounters = group_obj->MaxActiveCounters; 215 216 if (numCounters != NULL) 217 *numCounters = group_obj->NumCounters; 218 219 if (counters != NULL) { 220 unsigned i; 221 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize); 222 for (i = 0; i < n; i++) { 223 /* We just use the index in the Counters array as the ID. */ 224 counters[i] = i; 225 } 226 } 227 } 228 229 void GLAPIENTRY 230 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize, 231 GLsizei *length, GLchar *groupString) 232 { 233 GET_CURRENT_CONTEXT(ctx); 234 const struct gl_perf_monitor_group *group_obj; 235 236 init_groups(ctx); 237 238 group_obj = get_group(ctx, group); 239 if (group_obj == NULL) { 240 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD"); 241 return; 242 } 243 244 if (bufSize == 0) { 245 /* Return the number of characters that would be required to hold the 246 * group string, excluding the null terminator. 247 */ 248 if (length != NULL) 249 *length = strlen(group_obj->Name); 250 } else { 251 if (length != NULL) 252 *length = MIN2(strlen(group_obj->Name), bufSize); 253 if (groupString != NULL) 254 strncpy(groupString, group_obj->Name, bufSize); 255 } 256 } 257 258 void GLAPIENTRY 259 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter, 260 GLsizei bufSize, GLsizei *length, 261 GLchar *counterString) 262 { 263 GET_CURRENT_CONTEXT(ctx); 264 265 const struct gl_perf_monitor_group *group_obj; 266 const struct gl_perf_monitor_counter *counter_obj; 267 268 init_groups(ctx); 269 270 group_obj = get_group(ctx, group); 271 272 if (group_obj == NULL) { 273 _mesa_error(ctx, GL_INVALID_VALUE, 274 "glGetPerfMonitorCounterStringAMD(invalid group)"); 275 return; 276 } 277 278 counter_obj = get_counter(group_obj, counter); 279 280 if (counter_obj == NULL) { 281 _mesa_error(ctx, GL_INVALID_VALUE, 282 "glGetPerfMonitorCounterStringAMD(invalid counter)"); 283 return; 284 } 285 286 if (bufSize == 0) { 287 /* Return the number of characters that would be required to hold the 288 * counter string, excluding the null terminator. 289 */ 290 if (length != NULL) 291 *length = strlen(counter_obj->Name); 292 } else { 293 if (length != NULL) 294 *length = MIN2(strlen(counter_obj->Name), bufSize); 295 if (counterString != NULL) 296 strncpy(counterString, counter_obj->Name, bufSize); 297 } 298 } 299 300 void GLAPIENTRY 301 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname, 302 GLvoid *data) 303 { 304 GET_CURRENT_CONTEXT(ctx); 305 306 const struct gl_perf_monitor_group *group_obj; 307 const struct gl_perf_monitor_counter *counter_obj; 308 309 init_groups(ctx); 310 311 group_obj = get_group(ctx, group); 312 313 if (group_obj == NULL) { 314 _mesa_error(ctx, GL_INVALID_VALUE, 315 "glGetPerfMonitorCounterInfoAMD(invalid group)"); 316 return; 317 } 318 319 counter_obj = get_counter(group_obj, counter); 320 321 if (counter_obj == NULL) { 322 _mesa_error(ctx, GL_INVALID_VALUE, 323 "glGetPerfMonitorCounterInfoAMD(invalid counter)"); 324 return; 325 } 326 327 switch (pname) { 328 case GL_COUNTER_TYPE_AMD: 329 *((GLenum *) data) = counter_obj->Type; 330 break; 331 332 case GL_COUNTER_RANGE_AMD: 333 switch (counter_obj->Type) { 334 case GL_FLOAT: 335 case GL_PERCENTAGE_AMD: { 336 float *f_data = data; 337 f_data[0] = counter_obj->Minimum.f; 338 f_data[1] = counter_obj->Maximum.f; 339 break; 340 } 341 case GL_UNSIGNED_INT: { 342 uint32_t *u32_data = data; 343 u32_data[0] = counter_obj->Minimum.u32; 344 u32_data[1] = counter_obj->Maximum.u32; 345 break; 346 } 347 case GL_UNSIGNED_INT64_AMD: { 348 uint64_t *u64_data = data; 349 u64_data[0] = counter_obj->Minimum.u64; 350 u64_data[1] = counter_obj->Maximum.u64; 351 break; 352 } 353 default: 354 assert(!"Should not get here: invalid counter type"); 355 } 356 break; 357 358 default: 359 _mesa_error(ctx, GL_INVALID_ENUM, 360 "glGetPerfMonitorCounterInfoAMD(pname)"); 361 return; 362 } 363 } 364 365 void GLAPIENTRY 366 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors) 367 { 368 GLuint first; 369 GET_CURRENT_CONTEXT(ctx); 370 371 if (MESA_VERBOSE & VERBOSE_API) 372 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n); 373 374 init_groups(ctx); 375 376 if (n < 0) { 377 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)"); 378 return; 379 } 380 381 if (monitors == NULL) 382 return; 383 384 /* We don't actually need them to be contiguous, but this is what 385 * the rest of Mesa does, so we may as well. 386 */ 387 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n); 388 if (first) { 389 GLsizei i; 390 for (i = 0; i < n; i++) { 391 struct gl_perf_monitor_object *m = 392 new_performance_monitor(ctx, first + i); 393 if (!m) { 394 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); 395 return; 396 } 397 monitors[i] = first + i; 398 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m); 399 } 400 } else { 401 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); 402 return; 403 } 404 } 405 406 void GLAPIENTRY 407 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors) 408 { 409 GLint i; 410 GET_CURRENT_CONTEXT(ctx); 411 412 if (MESA_VERBOSE & VERBOSE_API) 413 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n); 414 415 if (n < 0) { 416 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)"); 417 return; 418 } 419 420 if (monitors == NULL) 421 return; 422 423 for (i = 0; i < n; i++) { 424 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]); 425 426 if (m) { 427 /* Give the driver a chance to stop the monitor if it's active. */ 428 if (m->Active) { 429 ctx->Driver.ResetPerfMonitor(ctx, m); 430 m->Ended = false; 431 } 432 433 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]); 434 ralloc_free(m->ActiveGroups); 435 ralloc_free(m->ActiveCounters); 436 ctx->Driver.DeletePerfMonitor(ctx, m); 437 } else { 438 /* "INVALID_VALUE error will be generated if any of the monitor IDs 439 * in the <monitors> parameter to DeletePerfMonitorsAMD do not 440 * reference a valid generated monitor ID." 441 */ 442 _mesa_error(ctx, GL_INVALID_VALUE, 443 "glDeletePerfMonitorsAMD(invalid monitor)"); 444 } 445 } 446 } 447 448 void GLAPIENTRY 449 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable, 450 GLuint group, GLint numCounters, 451 GLuint *counterList) 452 { 453 GET_CURRENT_CONTEXT(ctx); 454 int i; 455 struct gl_perf_monitor_object *m; 456 const struct gl_perf_monitor_group *group_obj; 457 458 m = lookup_monitor(ctx, monitor); 459 460 /* "INVALID_VALUE error will be generated if the <monitor> parameter to 461 * SelectPerfMonitorCountersAMD does not reference a monitor created by 462 * GenPerfMonitorsAMD." 463 */ 464 if (m == NULL) { 465 _mesa_error(ctx, GL_INVALID_VALUE, 466 "glSelectPerfMonitorCountersAMD(invalid monitor)"); 467 return; 468 } 469 470 group_obj = get_group(ctx, group); 471 472 /* "INVALID_VALUE error will be generated if the <group> parameter to 473 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD, 474 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or 475 * SelectPerfMonitorCountersAMD does not reference a valid group ID." 476 */ 477 if (group_obj == NULL) { 478 _mesa_error(ctx, GL_INVALID_VALUE, 479 "glSelectPerfMonitorCountersAMD(invalid group)"); 480 return; 481 } 482 483 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to 484 * SelectPerfMonitorCountersAMD is less than 0." 485 */ 486 if (numCounters < 0) { 487 _mesa_error(ctx, GL_INVALID_VALUE, 488 "glSelectPerfMonitorCountersAMD(numCounters < 0)"); 489 return; 490 } 491 492 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding 493 * results for that monitor become invalidated and the result queries 494 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0." 495 */ 496 ctx->Driver.ResetPerfMonitor(ctx, m); 497 498 /* Sanity check the counter ID list. */ 499 for (i = 0; i < numCounters; i++) { 500 if (counterList[i] >= group_obj->NumCounters) { 501 _mesa_error(ctx, GL_INVALID_VALUE, 502 "glSelectPerfMonitorCountersAMD(invalid counter ID)"); 503 return; 504 } 505 } 506 507 if (enable) { 508 /* Enable the counters */ 509 for (i = 0; i < numCounters; i++) { 510 ++m->ActiveGroups[group]; 511 BITSET_SET(m->ActiveCounters[group], counterList[i]); 512 } 513 } else { 514 /* Disable the counters */ 515 for (i = 0; i < numCounters; i++) { 516 --m->ActiveGroups[group]; 517 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]); 518 } 519 } 520 } 521 522 void GLAPIENTRY 523 _mesa_BeginPerfMonitorAMD(GLuint monitor) 524 { 525 GET_CURRENT_CONTEXT(ctx); 526 527 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 528 529 if (m == NULL) { 530 _mesa_error(ctx, GL_INVALID_VALUE, 531 "glBeginPerfMonitorAMD(invalid monitor)"); 532 return; 533 } 534 535 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is 536 * called when a performance monitor is already active." 537 */ 538 if (m->Active) { 539 _mesa_error(ctx, GL_INVALID_OPERATION, 540 "glBeginPerfMonitor(already active)"); 541 return; 542 } 543 544 /* The driver is free to return false if it can't begin monitoring for 545 * any reason. This translates into an INVALID_OPERATION error. 546 */ 547 if (ctx->Driver.BeginPerfMonitor(ctx, m)) { 548 m->Active = true; 549 m->Ended = false; 550 } else { 551 _mesa_error(ctx, GL_INVALID_OPERATION, 552 "glBeginPerfMonitor(driver unable to begin monitoring)"); 553 } 554 } 555 556 void GLAPIENTRY 557 _mesa_EndPerfMonitorAMD(GLuint monitor) 558 { 559 GET_CURRENT_CONTEXT(ctx); 560 561 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 562 563 if (m == NULL) { 564 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)"); 565 return; 566 } 567 568 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called 569 * when a performance monitor is not currently started." 570 */ 571 if (!m->Active) { 572 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)"); 573 return; 574 } 575 576 ctx->Driver.EndPerfMonitor(ctx, m); 577 578 m->Active = false; 579 m->Ended = true; 580 } 581 582 /** 583 * Return the number of bytes needed to store a monitor's result. 584 */ 585 static unsigned 586 perf_monitor_result_size(const struct gl_context *ctx, 587 const struct gl_perf_monitor_object *m) 588 { 589 unsigned group, counter; 590 unsigned size = 0; 591 592 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) { 593 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group]; 594 BITSET_WORD tmp; 595 596 BITSET_FOREACH_SET(counter, tmp, m->ActiveCounters[group], g->NumCounters) { 597 const struct gl_perf_monitor_counter *c = &g->Counters[counter]; 598 599 size += sizeof(uint32_t); /* Group ID */ 600 size += sizeof(uint32_t); /* Counter ID */ 601 size += _mesa_perf_monitor_counter_size(c); 602 } 603 } 604 return size; 605 } 606 607 void GLAPIENTRY 608 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname, 609 GLsizei dataSize, GLuint *data, 610 GLint *bytesWritten) 611 { 612 GET_CURRENT_CONTEXT(ctx); 613 614 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 615 bool result_available; 616 617 if (m == NULL) { 618 _mesa_error(ctx, GL_INVALID_VALUE, 619 "glGetPerfMonitorCounterDataAMD(invalid monitor)"); 620 return; 621 } 622 623 /* "It is an INVALID_OPERATION error for <data> to be NULL." */ 624 if (data == NULL) { 625 _mesa_error(ctx, GL_INVALID_OPERATION, 626 "glGetPerfMonitorCounterDataAMD(data == NULL)"); 627 return; 628 } 629 630 /* We need at least enough room for a single value. */ 631 if (dataSize < sizeof(GLuint)) { 632 if (bytesWritten != NULL) 633 *bytesWritten = 0; 634 return; 635 } 636 637 /* If the monitor has never ended, there is no result. */ 638 result_available = m->Ended && 639 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m); 640 641 /* AMD appears to return 0 for all queries unless a result is available. */ 642 if (!result_available) { 643 *data = 0; 644 if (bytesWritten != NULL) 645 *bytesWritten = sizeof(GLuint); 646 return; 647 } 648 649 switch (pname) { 650 case GL_PERFMON_RESULT_AVAILABLE_AMD: 651 *data = 1; 652 if (bytesWritten != NULL) 653 *bytesWritten = sizeof(GLuint); 654 break; 655 case GL_PERFMON_RESULT_SIZE_AMD: 656 *data = perf_monitor_result_size(ctx, m); 657 if (bytesWritten != NULL) 658 *bytesWritten = sizeof(GLuint); 659 break; 660 case GL_PERFMON_RESULT_AMD: 661 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten); 662 break; 663 default: 664 _mesa_error(ctx, GL_INVALID_ENUM, 665 "glGetPerfMonitorCounterDataAMD(pname)"); 666 } 667 } 668 669 /** 670 * Returns how many bytes a counter's value takes up. 671 */ 672 unsigned 673 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c) 674 { 675 switch (c->Type) { 676 case GL_FLOAT: 677 case GL_PERCENTAGE_AMD: 678 return sizeof(GLfloat); 679 case GL_UNSIGNED_INT: 680 return sizeof(GLuint); 681 case GL_UNSIGNED_INT64_AMD: 682 return sizeof(uint64_t); 683 default: 684 assert(!"Should not get here: invalid counter type"); 685 return 0; 686 } 687 } 688 689 extern void GLAPIENTRY 690 _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId) 691 { 692 GET_CURRENT_CONTEXT(ctx); 693 unsigned numGroups; 694 695 init_groups(ctx); 696 697 /* The GL_INTEL_performance_query spec says: 698 * 699 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated." 700 */ 701 if (!queryId) { 702 _mesa_error(ctx, GL_INVALID_VALUE, 703 "glGetFirstPerfQueryIdINTEL(queryId == NULL)"); 704 return; 705 } 706 707 numGroups = ctx->PerfMonitor.NumGroups; 708 709 /* The GL_INTEL_performance_query spec says: 710 * 711 * "If the given hardware platform doesn't support any performance 712 * queries, then the value of 0 is returned and INVALID_OPERATION error 713 * is raised." 714 */ 715 if (numGroups == 0) { 716 *queryId = 0; 717 _mesa_error(ctx, GL_INVALID_OPERATION, 718 "glGetFirstPerfQueryIdINTEL(no queries supported)"); 719 return; 720 } 721 722 *queryId = index_to_queryid(0); 723 } 724 725 extern void GLAPIENTRY 726 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId) 727 { 728 GET_CURRENT_CONTEXT(ctx); 729 init_groups(ctx); 730 731 /* The GL_INTEL_performance_query spec says: 732 * 733 * "The result is passed in location pointed by nextQueryId. If query 734 * identified by queryId is the last query available the value of 0 is 735 * returned. If the specified performance query identifier is invalid 736 * then INVALID_VALUE error is generated. If nextQueryId pointer is 737 * equal to 0, an INVALID_VALUE error is generated. Whenever error is 738 * generated, the value of 0 is returned." 739 */ 740 741 if (!nextQueryId) { 742 _mesa_error(ctx, GL_INVALID_VALUE, 743 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)"); 744 return; 745 } 746 747 if (!queryid_valid(ctx, queryId)) { 748 *nextQueryId = 0; 749 _mesa_error(ctx, GL_INVALID_VALUE, 750 "glGetNextPerfQueryIdINTEL(invalid query)"); 751 return; 752 } 753 754 ++queryId; 755 756 if (!queryid_valid(ctx, queryId)) { 757 *nextQueryId = 0; 758 } else { 759 *nextQueryId = queryId; 760 } 761 } 762 763 extern void GLAPIENTRY 764 _mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId) 765 { 766 GET_CURRENT_CONTEXT(ctx); 767 unsigned i; 768 769 init_groups(ctx); 770 771 /* The GL_INTEL_performance_query spec says: 772 * 773 * "If queryName does not reference a valid query name, an INVALID_VALUE 774 * error is generated." 775 */ 776 if (!queryName) { 777 _mesa_error(ctx, GL_INVALID_VALUE, 778 "glGetPerfQueryIdByNameINTEL(queryName == NULL)"); 779 return; 780 } 781 782 /* The specification does not state that this produces an error. */ 783 if (!queryId) { 784 _mesa_warning(ctx, "glGetPerfQueryIdByNameINTEL(queryId == NULL)"); 785 return; 786 } 787 788 for (i = 0; i < ctx->PerfMonitor.NumGroups; ++i) { 789 const struct gl_perf_monitor_group *group_obj = get_group(ctx, i); 790 if (strcmp(group_obj->Name, queryName) == 0) { 791 *queryId = index_to_queryid(i); 792 return; 793 } 794 } 795 796 _mesa_error(ctx, GL_INVALID_VALUE, 797 "glGetPerfQueryIdByNameINTEL(invalid query name)"); 798 } 799 800 extern void GLAPIENTRY 801 _mesa_GetPerfQueryInfoINTEL(GLuint queryId, 802 GLuint queryNameLength, char *queryName, 803 GLuint *dataSize, GLuint *noCounters, 804 GLuint *noActiveInstances, 805 GLuint *capsMask) 806 { 807 GET_CURRENT_CONTEXT(ctx); 808 unsigned i; 809 810 const struct gl_perf_monitor_group *group_obj; 811 812 init_groups(ctx); 813 814 group_obj = get_group(ctx, queryid_to_index(queryId)); 815 if (group_obj == NULL) { 816 /* The GL_INTEL_performance_query spec says: 817 * 818 * "If queryId does not reference a valid query type, an 819 * INVALID_VALUE error is generated." 820 */ 821 _mesa_error(ctx, GL_INVALID_VALUE, 822 "glGetPerfQueryInfoINTEL(invalid query)"); 823 return; 824 } 825 826 if (queryName) { 827 strncpy(queryName, group_obj->Name, queryNameLength); 828 829 /* No specification given about whether the string needs to be 830 * zero-terminated. Zero-terminate the string always as we don't 831 * otherwise communicate the length of the returned string. 832 */ 833 if (queryNameLength > 0) { 834 queryName[queryNameLength - 1] = '\0'; 835 } 836 } 837 838 if (dataSize) { 839 unsigned size = 0; 840 for (i = 0; i < group_obj->NumCounters; ++i) { 841 /* What we get from the driver is group id (uint32_t) + counter id 842 * (uint32_t) + value. 843 */ 844 size += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]); 845 } 846 *dataSize = size; 847 } 848 849 if (noCounters) { 850 *noCounters = group_obj->NumCounters; 851 } 852 853 /* The GL_INTEL_performance_query spec says: 854 * 855 * "-- the actual number of already created query instances in 856 * maxInstances location" 857 * 858 * 1) Typo in the specification, should be noActiveInstances. 859 * 2) Another typo in the specification, maxInstances parameter is not listed 860 * in the declaration of this function in the list of new functions. 861 */ 862 if (noActiveInstances) { 863 *noActiveInstances = _mesa_HashNumEntries(ctx->PerfMonitor.Monitors); 864 } 865 866 if (capsMask) { 867 /* TODO: This information not yet available in the monitor structs. For 868 * now, we hardcode SINGLE_CONTEXT, since that's what the implementation 869 * currently tries very hard to do. 870 */ 871 *capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL; 872 } 873 } 874 875 extern void GLAPIENTRY 876 _mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId, 877 GLuint counterNameLength, char *counterName, 878 GLuint counterDescLength, char *counterDesc, 879 GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, 880 GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue) 881 { 882 GET_CURRENT_CONTEXT(ctx); 883 884 const struct gl_perf_monitor_group *group_obj; 885 const struct gl_perf_monitor_counter *counter_obj; 886 unsigned counterIndex; 887 unsigned i; 888 889 init_groups(ctx); 890 891 group_obj = get_group(ctx, queryid_to_index(queryId)); 892 893 /* The GL_INTEL_performance_query spec says: 894 * 895 * "If the pair of queryId and counterId does not reference a valid 896 * counter, an INVALID_VALUE error is generated." 897 */ 898 if (group_obj == NULL) { 899 _mesa_error(ctx, GL_INVALID_VALUE, 900 "glGetPerfCounterInfoINTEL(invalid queryId)"); 901 return; 902 } 903 904 counterIndex = counterid_to_index(counterId); 905 counter_obj = get_counter(group_obj, counterIndex); 906 907 if (counter_obj == NULL) { 908 _mesa_error(ctx, GL_INVALID_VALUE, 909 "glGetPerfCounterInfoINTEL(invalid counterId)"); 910 return; 911 } 912 913 if (counterName) { 914 strncpy(counterName, counter_obj->Name, counterNameLength); 915 916 /* No specification given about whether the string needs to be 917 * zero-terminated. Zero-terminate the string always as we don't 918 * otherwise communicate the length of the returned string. 919 */ 920 if (counterNameLength > 0) { 921 counterName[counterNameLength - 1] = '\0'; 922 } 923 } 924 925 if (counterDesc) { 926 /* TODO: No separate description text at the moment. We pass the name 927 * again for the moment. 928 */ 929 strncpy(counterDesc, counter_obj->Name, counterDescLength); 930 931 /* No specification given about whether the string needs to be 932 * zero-terminated. Zero-terminate the string always as we don't 933 * otherwise communicate the length of the returned string. 934 */ 935 if (counterDescLength > 0) { 936 counterDesc[counterDescLength - 1] = '\0'; 937 } 938 } 939 940 if (counterOffset) { 941 unsigned offset = 0; 942 for (i = 0; i < counterIndex; ++i) { 943 /* What we get from the driver is group id (uint32_t) + counter id 944 * (uint32_t) + value. 945 */ 946 offset += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]); 947 } 948 *counterOffset = 2 * sizeof(uint32_t) + offset; 949 } 950 951 if (counterDataSize) { 952 *counterDataSize = _mesa_perf_monitor_counter_size(counter_obj); 953 } 954 955 if (counterTypeEnum) { 956 /* TODO: Different counter types (semantic type, not data type) not 957 * supported as of yet. 958 */ 959 *counterTypeEnum = GL_PERFQUERY_COUNTER_RAW_INTEL; 960 } 961 962 if (counterDataTypeEnum) { 963 switch (counter_obj->Type) { 964 case GL_FLOAT: 965 case GL_PERCENTAGE_AMD: 966 *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL; 967 break; 968 case GL_UNSIGNED_INT: 969 *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL; 970 break; 971 case GL_UNSIGNED_INT64_AMD: 972 *counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL; 973 break; 974 default: 975 assert(!"Should not get here: invalid counter type"); 976 return; 977 } 978 } 979 980 if (rawCounterMaxValue) { 981 /* This value is (implicitly) specified to be used only with 982 * GL_PERFQUERY_COUNTER_RAW_INTEL counters. When semantic types for 983 * counters are added, that needs to be checked. 984 */ 985 986 /* The GL_INTEL_performance_query spec says: 987 * 988 * "for some raw counters for which the maximal value is 989 * deterministic, the maximal value of the counter in 1 second is 990 * returned in the location pointed by rawCounterMaxValue, otherwise, 991 * the location is written with the value of 0." 992 * 993 * The maximum value reported by the driver at the moment is not with 994 * these semantics, so write 0 always to be safe. 995 */ 996 *rawCounterMaxValue = 0; 997 } 998 } 999 1000 extern void GLAPIENTRY 1001 _mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle) 1002 { 1003 GET_CURRENT_CONTEXT(ctx); 1004 GLuint first; 1005 GLuint group; 1006 const struct gl_perf_monitor_group *group_obj; 1007 struct gl_perf_monitor_object *m; 1008 unsigned i; 1009 1010 init_groups(ctx); 1011 1012 /* This is not specified in the extension, but is the only sane thing to 1013 * do. 1014 */ 1015 if (queryHandle == NULL) { 1016 _mesa_error(ctx, GL_INVALID_VALUE, 1017 "glCreatePerfQueryINTEL(queryHandle == NULL)"); 1018 return; 1019 } 1020 1021 group = queryid_to_index(queryId); 1022 group_obj = get_group(ctx, group); 1023 1024 /* The GL_INTEL_performance_query spec says: 1025 * 1026 * "If queryId does not reference a valid query type, an INVALID_VALUE 1027 * error is generated." 1028 */ 1029 if (group_obj == NULL) { 1030 _mesa_error(ctx, GL_INVALID_VALUE, 1031 "glCreatePerfQueryINTEL(invalid queryId)"); 1032 return; 1033 } 1034 1035 /* The query object created here is the counterpart of a `monitor' in 1036 * AMD_performance_monitor. This call is equivalent to calling 1037 * GenPerfMonitorsAMD and SelectPerfMonitorCountersAMD with a list of all 1038 * counters in a group. 1039 */ 1040 1041 /* We keep the monitor ids contiguous */ 1042 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, 1); 1043 if (!first) { 1044 /* The GL_INTEL_performance_query spec says: 1045 * 1046 * "If the query instance cannot be created due to exceeding the 1047 * number of allowed instances or driver fails query creation due to 1048 * an insufficient memory reason, an OUT_OF_MEMORY error is 1049 * generated, and the location pointed by queryHandle returns NULL." 1050 */ 1051 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCreatePerfQueryINTEL"); 1052 return; 1053 } 1054 1055 m = new_performance_monitor(ctx, first); 1056 if (m == NULL) { 1057 _mesa_error_no_memory(__func__); 1058 return; 1059 } 1060 1061 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first, m); 1062 *queryHandle = first; 1063 1064 ctx->Driver.ResetPerfMonitor(ctx, m); 1065 1066 for (i = 0; i < group_obj->NumCounters; ++i) { 1067 ++m->ActiveGroups[group]; 1068 /* Counters are a continuous range of integers, 0 to NumCounters (excl), 1069 * so i is the counter value to use here. 1070 */ 1071 BITSET_SET(m->ActiveCounters[group], i); 1072 } 1073 } 1074 1075 extern void GLAPIENTRY 1076 _mesa_DeletePerfQueryINTEL(GLuint queryHandle) 1077 { 1078 GET_CURRENT_CONTEXT(ctx); 1079 struct gl_perf_monitor_object *m; 1080 1081 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor 1082 * id. 1083 */ 1084 m = lookup_monitor(ctx, queryHandle); 1085 1086 /* The GL_INTEL_performance_query spec says: 1087 * 1088 * "If a query handle doesn't reference a previously created performance 1089 * query instance, an INVALID_VALUE error is generated." 1090 */ 1091 if (m == NULL) { 1092 _mesa_error(ctx, GL_INVALID_VALUE, 1093 "glDeletePerfQueryINTEL(invalid queryHandle)"); 1094 return; 1095 } 1096 1097 /* Let the driver stop the monitor if it's active. */ 1098 if (m->Active) { 1099 ctx->Driver.ResetPerfMonitor(ctx, m); 1100 m->Ended = false; 1101 } 1102 1103 _mesa_HashRemove(ctx->PerfMonitor.Monitors, queryHandle); 1104 ralloc_free(m->ActiveGroups); 1105 ralloc_free(m->ActiveCounters); 1106 ctx->Driver.DeletePerfMonitor(ctx, m); 1107 } 1108 1109 extern void GLAPIENTRY 1110 _mesa_BeginPerfQueryINTEL(GLuint queryHandle) 1111 { 1112 GET_CURRENT_CONTEXT(ctx); 1113 struct gl_perf_monitor_object *m; 1114 1115 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor 1116 * id. 1117 */ 1118 1119 m = lookup_monitor(ctx, queryHandle); 1120 1121 /* The GL_INTEL_performance_query spec says: 1122 * 1123 * "If a query handle doesn't reference a previously created performance 1124 * query instance, an INVALID_VALUE error is generated." 1125 */ 1126 if (m == NULL) { 1127 _mesa_error(ctx, GL_INVALID_VALUE, 1128 "glBeginPerfQueryINTEL(invalid queryHandle)"); 1129 return; 1130 } 1131 1132 /* The GL_INTEL_performance_query spec says: 1133 * 1134 * "Note that some query types, they cannot be collected in the same 1135 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if 1136 * they refer to queries of such different types. In such case 1137 * INVALID_OPERATION error is generated." 1138 * 1139 * We also generate an INVALID_OPERATION error if the driver can't begin 1140 * monitoring for its own reasons, and for nesting the same query. 1141 */ 1142 if (m->Active) { 1143 _mesa_error(ctx, GL_INVALID_OPERATION, 1144 "glBeginPerfQueryINTEL(already active)"); 1145 return; 1146 } 1147 1148 if (ctx->Driver.BeginPerfMonitor(ctx, m)) { 1149 m->Active = true; 1150 m->Ended = false; 1151 } else { 1152 _mesa_error(ctx, GL_INVALID_OPERATION, 1153 "glBeginPerfQueryINTEL(driver unable to begin monitoring)"); 1154 } 1155 } 1156 1157 extern void GLAPIENTRY 1158 _mesa_EndPerfQueryINTEL(GLuint queryHandle) 1159 { 1160 GET_CURRENT_CONTEXT(ctx); 1161 struct gl_perf_monitor_object *m; 1162 1163 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor 1164 * id. 1165 */ 1166 1167 m = lookup_monitor(ctx, queryHandle); 1168 1169 /* The GL_INTEL_performance_query spec says: 1170 * 1171 * "If a performance query is not currently started, an 1172 * INVALID_OPERATION error will be generated." 1173 * 1174 * The specification doesn't state that an invalid handle would be an 1175 * INVALID_VALUE error. Regardless, query for such a handle will not be 1176 * started, so we generate an INVALID_OPERATION in that case too. 1177 */ 1178 if (m == NULL) { 1179 _mesa_error(ctx, GL_INVALID_OPERATION, 1180 "glEndPerfQueryINTEL(invalid queryHandle)"); 1181 return; 1182 } 1183 1184 if (!m->Active) { 1185 _mesa_error(ctx, GL_INVALID_OPERATION, 1186 "glEndPerfQueryINTEL(not active)"); 1187 return; 1188 } 1189 1190 ctx->Driver.EndPerfMonitor(ctx, m); 1191 1192 m->Active = false; 1193 m->Ended = true; 1194 } 1195 1196 extern void GLAPIENTRY 1197 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags, 1198 GLsizei dataSize, void *data, GLuint *bytesWritten) 1199 { 1200 GET_CURRENT_CONTEXT(ctx); 1201 struct gl_perf_monitor_object *m; 1202 bool result_available; 1203 1204 /* The GL_INTEL_performance_query spec says: 1205 * 1206 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE 1207 * error is generated." 1208 */ 1209 if (!bytesWritten || !data) { 1210 _mesa_error(ctx, GL_INVALID_VALUE, 1211 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)"); 1212 return; 1213 } 1214 1215 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor 1216 * id. 1217 */ 1218 1219 m = lookup_monitor(ctx, queryHandle); 1220 1221 /* The specification doesn't state that an invalid handle generates an 1222 * error. We could interpret that to mean the case should be handled as 1223 * "measurement not ready for this query", but what should be done if 1224 * `flags' equals PERFQUERY_WAIT_INTEL? 1225 * 1226 * To resolve this, we just generate an INVALID_VALUE from an invalid query 1227 * handle. 1228 */ 1229 if (m == NULL) { 1230 _mesa_error(ctx, GL_INVALID_VALUE, 1231 "glGetPerfQueryDataINTEL(invalid queryHandle)"); 1232 return; 1233 } 1234 1235 /* We need at least enough room for a single value. */ 1236 if (dataSize < sizeof(GLuint)) { 1237 *bytesWritten = 0; 1238 return; 1239 } 1240 1241 /* The GL_INTEL_performance_query spec says: 1242 * 1243 * "The call may end without returning any data if they are not ready 1244 * for reading as the measurement session is still pending (the 1245 * EndPerfQueryINTEL() command processing is not finished by 1246 * hardware). In this case location pointed by the bytesWritten 1247 * parameter will be set to 0." 1248 * 1249 * If EndPerfQueryINTEL() is not called at all, we follow this. 1250 */ 1251 if (!m->Ended) { 1252 *bytesWritten = 0; 1253 return; 1254 } 1255 1256 result_available = ctx->Driver.IsPerfMonitorResultAvailable(ctx, m); 1257 1258 if (!result_available) { 1259 if (flags == GL_PERFQUERY_FLUSH_INTEL) { 1260 ctx->Driver.Flush(ctx); 1261 } else if (flags == GL_PERFQUERY_WAIT_INTEL) { 1262 /* Assume Finish() is both enough and not too much to wait for 1263 * results. If results are still not available after Finish(), the 1264 * later code automatically bails out with 0 for bytesWritten. 1265 */ 1266 ctx->Driver.Finish(ctx); 1267 result_available = 1268 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m); 1269 } 1270 } 1271 1272 if (result_available) { 1273 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, (GLint*)bytesWritten); 1274 } else { 1275 *bytesWritten = 0; 1276 } 1277 } 1278