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#include "util/u_memory.h" 48#include "api_exec_decl.h" 49 50#include "state_tracker/st_cb_bitmap.h" 51#include "state_tracker/st_context.h" 52#include "state_tracker/st_debug.h" 53 54#include "pipe/p_context.h" 55#include "pipe/p_screen.h" 56 57void 58_mesa_init_performance_monitors(struct gl_context *ctx) 59{ 60 ctx->PerfMonitor.Monitors = _mesa_NewHashTable(); 61 ctx->PerfMonitor.NumGroups = 0; 62 ctx->PerfMonitor.Groups = NULL; 63} 64 65 66static bool 67init_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 68{ 69 struct pipe_context *pipe = ctx->pipe; 70 unsigned *batch = NULL; 71 unsigned num_active_counters = 0; 72 unsigned max_batch_counters = 0; 73 unsigned num_batch_counters = 0; 74 int gid, cid; 75 76 st_flush_bitmap_cache(st_context(ctx)); 77 78 /* Determine the number of active counters. */ 79 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) { 80 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid]; 81 82 if (m->ActiveGroups[gid] > g->MaxActiveCounters) { 83 /* Maximum number of counters reached. Cannot start the session. */ 84 if (ST_DEBUG & DEBUG_MESA) { 85 debug_printf("Maximum number of counters reached. " 86 "Cannot start the session!\n"); 87 } 88 return false; 89 } 90 91 num_active_counters += m->ActiveGroups[gid]; 92 if (g->has_batch) 93 max_batch_counters += m->ActiveGroups[gid]; 94 } 95 96 if (!num_active_counters) 97 return true; 98 99 m->active_counters = CALLOC(num_active_counters, 100 sizeof(*m->active_counters)); 101 if (!m->active_counters) 102 return false; 103 104 if (max_batch_counters) { 105 batch = CALLOC(max_batch_counters, sizeof(*batch)); 106 if (!batch) 107 return false; 108 } 109 110 /* Create a query for each active counter. */ 111 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) { 112 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid]; 113 114 BITSET_FOREACH_SET(cid, m->ActiveCounters[gid], g->NumCounters) { 115 const struct gl_perf_monitor_counter *c = &g->Counters[cid]; 116 struct gl_perf_counter_object *cntr = 117 &m->active_counters[m->num_active_counters]; 118 119 cntr->id = cid; 120 cntr->group_id = gid; 121 if (c->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) { 122 cntr->batch_index = num_batch_counters; 123 batch[num_batch_counters++] = c->query_type; 124 } else { 125 cntr->query = pipe->create_query(pipe, c->query_type, 0); 126 if (!cntr->query) 127 goto fail; 128 } 129 ++m->num_active_counters; 130 } 131 } 132 133 /* Create the batch query. */ 134 if (num_batch_counters) { 135 m->batch_query = pipe->create_batch_query(pipe, num_batch_counters, 136 batch); 137 m->batch_result = CALLOC(num_batch_counters, sizeof(m->batch_result->batch[0])); 138 if (!m->batch_query || !m->batch_result) 139 goto fail; 140 } 141 142 FREE(batch); 143 return true; 144 145fail: 146 FREE(batch); 147 return false; 148} 149 150static void 151do_reset_perf_monitor(struct gl_perf_monitor_object *m, 152 struct pipe_context *pipe) 153{ 154 unsigned i; 155 156 for (i = 0; i < m->num_active_counters; ++i) { 157 struct pipe_query *query = m->active_counters[i].query; 158 if (query) 159 pipe->destroy_query(pipe, query); 160 } 161 FREE(m->active_counters); 162 m->active_counters = NULL; 163 m->num_active_counters = 0; 164 165 if (m->batch_query) { 166 pipe->destroy_query(pipe, m->batch_query); 167 m->batch_query = NULL; 168 } 169 FREE(m->batch_result); 170 m->batch_result = NULL; 171} 172 173static void 174delete_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 175{ 176 struct pipe_context *pipe = st_context(ctx)->pipe; 177 178 do_reset_perf_monitor(m, pipe); 179 FREE(m); 180} 181 182static GLboolean 183begin_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 184{ 185 struct pipe_context *pipe = st_context(ctx)->pipe; 186 unsigned i; 187 188 if (!m->num_active_counters) { 189 /* Create a query for each active counter before starting 190 * a new monitoring session. */ 191 if (!init_perf_monitor(ctx, m)) 192 goto fail; 193 } 194 195 /* Start the query for each active counter. */ 196 for (i = 0; i < m->num_active_counters; ++i) { 197 struct pipe_query *query = m->active_counters[i].query; 198 if (query && !pipe->begin_query(pipe, query)) 199 goto fail; 200 } 201 202 if (m->batch_query && !pipe->begin_query(pipe, m->batch_query)) 203 goto fail; 204 205 return true; 206 207fail: 208 /* Failed to start the monitoring session. */ 209 do_reset_perf_monitor(m, pipe); 210 return false; 211} 212 213static void 214end_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 215{ 216 struct pipe_context *pipe = st_context(ctx)->pipe; 217 unsigned i; 218 219 /* Stop the query for each active counter. */ 220 for (i = 0; i < m->num_active_counters; ++i) { 221 struct pipe_query *query = m->active_counters[i].query; 222 if (query) 223 pipe->end_query(pipe, query); 224 } 225 226 if (m->batch_query) 227 pipe->end_query(pipe, m->batch_query); 228} 229 230static void 231reset_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 232{ 233 struct pipe_context *pipe = st_context(ctx)->pipe; 234 235 if (!m->Ended) 236 end_perf_monitor(ctx, m); 237 238 do_reset_perf_monitor(m, pipe); 239 240 if (m->Active) 241 begin_perf_monitor(ctx, m); 242} 243 244static GLboolean 245is_perf_monitor_result_available(struct gl_context *ctx, 246 struct gl_perf_monitor_object *m) 247{ 248 struct pipe_context *pipe = st_context(ctx)->pipe; 249 unsigned i; 250 251 if (!m->num_active_counters) 252 return false; 253 254 /* The result of a monitoring session is only available if the query of 255 * each active counter is idle. */ 256 for (i = 0; i < m->num_active_counters; ++i) { 257 struct pipe_query *query = m->active_counters[i].query; 258 union pipe_query_result result; 259 if (query && !pipe->get_query_result(pipe, query, FALSE, &result)) { 260 /* The query is busy. */ 261 return false; 262 } 263 } 264 265 if (m->batch_query && 266 !pipe->get_query_result(pipe, m->batch_query, FALSE, m->batch_result)) 267 return false; 268 269 return true; 270} 271 272static void 273get_perf_monitor_result(struct gl_context *ctx, 274 struct gl_perf_monitor_object *m, 275 GLsizei dataSize, 276 GLuint *data, 277 GLint *bytesWritten) 278{ 279 struct pipe_context *pipe = st_context(ctx)->pipe; 280 unsigned i; 281 282 /* Copy data to the supplied array (data). 283 * 284 * The output data format is: <group ID, counter ID, value> for each 285 * active counter. The API allows counters to appear in any order. 286 */ 287 GLsizei offset = 0; 288 bool have_batch_query = false; 289 290 if (m->batch_query) 291 have_batch_query = pipe->get_query_result(pipe, m->batch_query, TRUE, 292 m->batch_result); 293 294 /* Read query results for each active counter. */ 295 for (i = 0; i < m->num_active_counters; ++i) { 296 struct gl_perf_counter_object *cntr = &m->active_counters[i]; 297 union pipe_query_result result = { 0 }; 298 int gid, cid; 299 GLenum type; 300 301 cid = cntr->id; 302 gid = cntr->group_id; 303 type = ctx->PerfMonitor.Groups[gid].Counters[cid].Type; 304 305 if (cntr->query) { 306 if (!pipe->get_query_result(pipe, cntr->query, TRUE, &result)) 307 continue; 308 } else { 309 if (!have_batch_query) 310 continue; 311 result.batch[0] = m->batch_result->batch[cntr->batch_index]; 312 } 313 314 data[offset++] = gid; 315 data[offset++] = cid; 316 switch (type) { 317 case GL_UNSIGNED_INT64_AMD: 318 memcpy(&data[offset], &result.u64, sizeof(uint64_t)); 319 offset += sizeof(uint64_t) / sizeof(GLuint); 320 break; 321 case GL_UNSIGNED_INT: 322 memcpy(&data[offset], &result.u32, sizeof(uint32_t)); 323 offset += sizeof(uint32_t) / sizeof(GLuint); 324 break; 325 case GL_FLOAT: 326 case GL_PERCENTAGE_AMD: 327 memcpy(&data[offset], &result.f, sizeof(GLfloat)); 328 offset += sizeof(GLfloat) / sizeof(GLuint); 329 break; 330 } 331 } 332 333 if (bytesWritten) 334 *bytesWritten = offset * sizeof(GLuint); 335} 336 337void 338_mesa_free_perfomance_monitor_groups(struct gl_context *ctx) 339{ 340 struct gl_perf_monitor_state *perfmon = &ctx->PerfMonitor; 341 int gid; 342 343 for (gid = 0; gid < perfmon->NumGroups; gid++) { 344 FREE((void *)perfmon->Groups[gid].Counters); 345 } 346 FREE((void *)perfmon->Groups); 347} 348 349static inline void 350init_groups(struct gl_context *ctx) 351{ 352 if (likely(ctx->PerfMonitor.Groups)) 353 return; 354 355 struct gl_perf_monitor_state *perfmon = &ctx->PerfMonitor; 356 struct pipe_screen *screen = ctx->pipe->screen; 357 struct gl_perf_monitor_group *groups = NULL; 358 int num_counters, num_groups; 359 int gid, cid; 360 361 /* Get the number of available queries. */ 362 num_counters = screen->get_driver_query_info(screen, 0, NULL); 363 364 /* Get the number of available groups. */ 365 num_groups = screen->get_driver_query_group_info(screen, 0, NULL); 366 groups = CALLOC(num_groups, sizeof(*groups)); 367 if (!groups) 368 return; 369 370 for (gid = 0; gid < num_groups; gid++) { 371 struct gl_perf_monitor_group *g = &groups[perfmon->NumGroups]; 372 struct pipe_driver_query_group_info group_info; 373 struct gl_perf_monitor_counter *counters = NULL; 374 375 if (!screen->get_driver_query_group_info(screen, gid, &group_info)) 376 continue; 377 378 g->Name = group_info.name; 379 g->MaxActiveCounters = group_info.max_active_queries; 380 381 if (group_info.num_queries) 382 counters = CALLOC(group_info.num_queries, sizeof(*counters)); 383 if (!counters) 384 goto fail; 385 g->Counters = counters; 386 387 for (cid = 0; cid < num_counters; cid++) { 388 struct gl_perf_monitor_counter *c = &counters[g->NumCounters]; 389 struct pipe_driver_query_info info; 390 391 if (!screen->get_driver_query_info(screen, cid, &info)) 392 continue; 393 if (info.group_id != gid) 394 continue; 395 396 c->Name = info.name; 397 switch (info.type) { 398 case PIPE_DRIVER_QUERY_TYPE_UINT64: 399 case PIPE_DRIVER_QUERY_TYPE_BYTES: 400 case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS: 401 case PIPE_DRIVER_QUERY_TYPE_HZ: 402 c->Minimum.u64 = 0; 403 c->Maximum.u64 = info.max_value.u64 ? info.max_value.u64 : UINT64_MAX; 404 c->Type = GL_UNSIGNED_INT64_AMD; 405 break; 406 case PIPE_DRIVER_QUERY_TYPE_UINT: 407 c->Minimum.u32 = 0; 408 c->Maximum.u32 = info.max_value.u32 ? info.max_value.u32 : UINT32_MAX; 409 c->Type = GL_UNSIGNED_INT; 410 break; 411 case PIPE_DRIVER_QUERY_TYPE_FLOAT: 412 c->Minimum.f = 0.0; 413 c->Maximum.f = info.max_value.f ? info.max_value.f : FLT_MAX; 414 c->Type = GL_FLOAT; 415 break; 416 case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE: 417 c->Minimum.f = 0.0f; 418 c->Maximum.f = 100.0f; 419 c->Type = GL_PERCENTAGE_AMD; 420 break; 421 default: 422 unreachable("Invalid driver query type!"); 423 } 424 425 c->query_type = info.query_type; 426 c->flags = info.flags; 427 if (c->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) 428 g->has_batch = true; 429 430 g->NumCounters++; 431 } 432 perfmon->NumGroups++; 433 } 434 perfmon->Groups = groups; 435 436 return; 437 438fail: 439 for (gid = 0; gid < num_groups; gid++) { 440 FREE((void *)groups[gid].Counters); 441 } 442 FREE(groups); 443} 444 445static struct gl_perf_monitor_object * 446new_performance_monitor(struct gl_context *ctx, GLuint index) 447{ 448 unsigned i; 449 struct gl_perf_monitor_object *m = CALLOC_STRUCT(gl_perf_monitor_object); 450 451 if (m == NULL) 452 return NULL; 453 454 m->Name = index; 455 456 m->Active = false; 457 458 m->ActiveGroups = 459 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups); 460 461 m->ActiveCounters = 462 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups); 463 464 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL) 465 goto fail; 466 467 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) { 468 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i]; 469 470 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD, 471 BITSET_WORDS(g->NumCounters)); 472 if (m->ActiveCounters[i] == NULL) 473 goto fail; 474 } 475 476 return m; 477 478fail: 479 ralloc_free(m->ActiveGroups); 480 ralloc_free(m->ActiveCounters); 481 delete_perf_monitor(ctx, m); 482 return NULL; 483} 484 485static void 486free_performance_monitor(void *data, void *user) 487{ 488 struct gl_perf_monitor_object *m = data; 489 struct gl_context *ctx = user; 490 491 ralloc_free(m->ActiveGroups); 492 ralloc_free(m->ActiveCounters); 493 delete_perf_monitor(ctx, m); 494} 495 496void 497_mesa_free_performance_monitors(struct gl_context *ctx) 498{ 499 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors, 500 free_performance_monitor, ctx); 501 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors); 502} 503 504static inline struct gl_perf_monitor_object * 505lookup_monitor(struct gl_context *ctx, GLuint id) 506{ 507 return (struct gl_perf_monitor_object *) 508 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id); 509} 510 511static inline const struct gl_perf_monitor_group * 512get_group(const struct gl_context *ctx, GLuint id) 513{ 514 if (id >= ctx->PerfMonitor.NumGroups) 515 return NULL; 516 517 return &ctx->PerfMonitor.Groups[id]; 518} 519 520static inline const struct gl_perf_monitor_counter * 521get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id) 522{ 523 if (id >= group_obj->NumCounters) 524 return NULL; 525 526 return &group_obj->Counters[id]; 527} 528 529/*****************************************************************************/ 530 531void GLAPIENTRY 532_mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize, 533 GLuint *groups) 534{ 535 GET_CURRENT_CONTEXT(ctx); 536 init_groups(ctx); 537 538 if (numGroups != NULL) 539 *numGroups = ctx->PerfMonitor.NumGroups; 540 541 if (groupsSize > 0 && groups != NULL) { 542 unsigned i; 543 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups); 544 545 /* We just use the index in the Groups array as the ID. */ 546 for (i = 0; i < n; i++) 547 groups[i] = i; 548 } 549} 550 551void GLAPIENTRY 552_mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters, 553 GLint *maxActiveCounters, 554 GLsizei countersSize, GLuint *counters) 555{ 556 GET_CURRENT_CONTEXT(ctx); 557 const struct gl_perf_monitor_group *group_obj; 558 559 init_groups(ctx); 560 561 group_obj = get_group(ctx, group); 562 if (group_obj == NULL) { 563 _mesa_error(ctx, GL_INVALID_VALUE, 564 "glGetPerfMonitorCountersAMD(invalid group)"); 565 return; 566 } 567 568 if (maxActiveCounters != NULL) 569 *maxActiveCounters = group_obj->MaxActiveCounters; 570 571 if (numCounters != NULL) 572 *numCounters = group_obj->NumCounters; 573 574 if (counters != NULL) { 575 unsigned i; 576 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize); 577 for (i = 0; i < n; i++) { 578 /* We just use the index in the Counters array as the ID. */ 579 counters[i] = i; 580 } 581 } 582} 583 584void GLAPIENTRY 585_mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize, 586 GLsizei *length, GLchar *groupString) 587{ 588 GET_CURRENT_CONTEXT(ctx); 589 const struct gl_perf_monitor_group *group_obj; 590 591 init_groups(ctx); 592 593 group_obj = get_group(ctx, group); 594 if (group_obj == NULL) { 595 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD"); 596 return; 597 } 598 599 if (bufSize == 0) { 600 /* Return the number of characters that would be required to hold the 601 * group string, excluding the null terminator. 602 */ 603 if (length != NULL) 604 *length = strlen(group_obj->Name); 605 } else { 606 if (length != NULL) 607 *length = MIN2(strlen(group_obj->Name), bufSize); 608 if (groupString != NULL) 609 strncpy(groupString, group_obj->Name, bufSize); 610 } 611} 612 613void GLAPIENTRY 614_mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter, 615 GLsizei bufSize, GLsizei *length, 616 GLchar *counterString) 617{ 618 GET_CURRENT_CONTEXT(ctx); 619 620 const struct gl_perf_monitor_group *group_obj; 621 const struct gl_perf_monitor_counter *counter_obj; 622 623 init_groups(ctx); 624 625 group_obj = get_group(ctx, group); 626 627 if (group_obj == NULL) { 628 _mesa_error(ctx, GL_INVALID_VALUE, 629 "glGetPerfMonitorCounterStringAMD(invalid group)"); 630 return; 631 } 632 633 counter_obj = get_counter(group_obj, counter); 634 635 if (counter_obj == NULL) { 636 _mesa_error(ctx, GL_INVALID_VALUE, 637 "glGetPerfMonitorCounterStringAMD(invalid counter)"); 638 return; 639 } 640 641 if (bufSize == 0) { 642 /* Return the number of characters that would be required to hold the 643 * counter string, excluding the null terminator. 644 */ 645 if (length != NULL) 646 *length = strlen(counter_obj->Name); 647 } else { 648 if (length != NULL) 649 *length = MIN2(strlen(counter_obj->Name), bufSize); 650 if (counterString != NULL) 651 strncpy(counterString, counter_obj->Name, bufSize); 652 } 653} 654 655void GLAPIENTRY 656_mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname, 657 GLvoid *data) 658{ 659 GET_CURRENT_CONTEXT(ctx); 660 661 const struct gl_perf_monitor_group *group_obj; 662 const struct gl_perf_monitor_counter *counter_obj; 663 664 init_groups(ctx); 665 666 group_obj = get_group(ctx, group); 667 668 if (group_obj == NULL) { 669 _mesa_error(ctx, GL_INVALID_VALUE, 670 "glGetPerfMonitorCounterInfoAMD(invalid group)"); 671 return; 672 } 673 674 counter_obj = get_counter(group_obj, counter); 675 676 if (counter_obj == NULL) { 677 _mesa_error(ctx, GL_INVALID_VALUE, 678 "glGetPerfMonitorCounterInfoAMD(invalid counter)"); 679 return; 680 } 681 682 switch (pname) { 683 case GL_COUNTER_TYPE_AMD: 684 *((GLenum *) data) = counter_obj->Type; 685 break; 686 687 case GL_COUNTER_RANGE_AMD: 688 switch (counter_obj->Type) { 689 case GL_FLOAT: 690 case GL_PERCENTAGE_AMD: { 691 float *f_data = data; 692 f_data[0] = counter_obj->Minimum.f; 693 f_data[1] = counter_obj->Maximum.f; 694 break; 695 } 696 case GL_UNSIGNED_INT: { 697 uint32_t *u32_data = data; 698 u32_data[0] = counter_obj->Minimum.u32; 699 u32_data[1] = counter_obj->Maximum.u32; 700 break; 701 } 702 case GL_UNSIGNED_INT64_AMD: { 703 uint64_t *u64_data = data; 704 u64_data[0] = counter_obj->Minimum.u64; 705 u64_data[1] = counter_obj->Maximum.u64; 706 break; 707 } 708 default: 709 assert(!"Should not get here: invalid counter type"); 710 } 711 break; 712 713 default: 714 _mesa_error(ctx, GL_INVALID_ENUM, 715 "glGetPerfMonitorCounterInfoAMD(pname)"); 716 return; 717 } 718} 719 720void GLAPIENTRY 721_mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors) 722{ 723 GET_CURRENT_CONTEXT(ctx); 724 725 if (MESA_VERBOSE & VERBOSE_API) 726 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n); 727 728 init_groups(ctx); 729 730 if (n < 0) { 731 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)"); 732 return; 733 } 734 735 if (monitors == NULL) 736 return; 737 738 if (_mesa_HashFindFreeKeys(ctx->PerfMonitor.Monitors, monitors, n)) { 739 GLsizei i; 740 for (i = 0; i < n; i++) { 741 struct gl_perf_monitor_object *m = 742 new_performance_monitor(ctx, monitors[i]); 743 if (!m) { 744 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); 745 return; 746 } 747 _mesa_HashInsert(ctx->PerfMonitor.Monitors, monitors[i], m, true); 748 } 749 } else { 750 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); 751 return; 752 } 753} 754 755void GLAPIENTRY 756_mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors) 757{ 758 GLint i; 759 GET_CURRENT_CONTEXT(ctx); 760 761 if (MESA_VERBOSE & VERBOSE_API) 762 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n); 763 764 if (n < 0) { 765 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)"); 766 return; 767 } 768 769 if (monitors == NULL) 770 return; 771 772 for (i = 0; i < n; i++) { 773 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]); 774 775 if (m) { 776 /* Give the driver a chance to stop the monitor if it's active. */ 777 if (m->Active) { 778 reset_perf_monitor(ctx, m); 779 m->Ended = false; 780 } 781 782 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]); 783 ralloc_free(m->ActiveGroups); 784 ralloc_free(m->ActiveCounters); 785 delete_perf_monitor(ctx, m); 786 } else { 787 /* "INVALID_VALUE error will be generated if any of the monitor IDs 788 * in the <monitors> parameter to DeletePerfMonitorsAMD do not 789 * reference a valid generated monitor ID." 790 */ 791 _mesa_error(ctx, GL_INVALID_VALUE, 792 "glDeletePerfMonitorsAMD(invalid monitor)"); 793 } 794 } 795} 796 797void GLAPIENTRY 798_mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable, 799 GLuint group, GLint numCounters, 800 GLuint *counterList) 801{ 802 GET_CURRENT_CONTEXT(ctx); 803 int i; 804 struct gl_perf_monitor_object *m; 805 const struct gl_perf_monitor_group *group_obj; 806 807 m = lookup_monitor(ctx, monitor); 808 809 /* "INVALID_VALUE error will be generated if the <monitor> parameter to 810 * SelectPerfMonitorCountersAMD does not reference a monitor created by 811 * GenPerfMonitorsAMD." 812 */ 813 if (m == NULL) { 814 _mesa_error(ctx, GL_INVALID_VALUE, 815 "glSelectPerfMonitorCountersAMD(invalid monitor)"); 816 return; 817 } 818 819 group_obj = get_group(ctx, group); 820 821 /* "INVALID_VALUE error will be generated if the <group> parameter to 822 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD, 823 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or 824 * SelectPerfMonitorCountersAMD does not reference a valid group ID." 825 */ 826 if (group_obj == NULL) { 827 _mesa_error(ctx, GL_INVALID_VALUE, 828 "glSelectPerfMonitorCountersAMD(invalid group)"); 829 return; 830 } 831 832 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to 833 * SelectPerfMonitorCountersAMD is less than 0." 834 */ 835 if (numCounters < 0) { 836 _mesa_error(ctx, GL_INVALID_VALUE, 837 "glSelectPerfMonitorCountersAMD(numCounters < 0)"); 838 return; 839 } 840 841 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding 842 * results for that monitor become invalidated and the result queries 843 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0." 844 */ 845 reset_perf_monitor(ctx, m); 846 847 /* Sanity check the counter ID list. */ 848 for (i = 0; i < numCounters; i++) { 849 if (counterList[i] >= group_obj->NumCounters) { 850 _mesa_error(ctx, GL_INVALID_VALUE, 851 "glSelectPerfMonitorCountersAMD(invalid counter ID)"); 852 return; 853 } 854 } 855 856 if (enable) { 857 /* Enable the counters */ 858 for (i = 0; i < numCounters; i++) { 859 if (!BITSET_TEST(m->ActiveCounters[group], counterList[i])) { 860 ++m->ActiveGroups[group]; 861 BITSET_SET(m->ActiveCounters[group], counterList[i]); 862 } 863 } 864 } else { 865 /* Disable the counters */ 866 for (i = 0; i < numCounters; i++) { 867 if (BITSET_TEST(m->ActiveCounters[group], counterList[i])) { 868 --m->ActiveGroups[group]; 869 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]); 870 } 871 } 872 } 873} 874 875void GLAPIENTRY 876_mesa_BeginPerfMonitorAMD(GLuint monitor) 877{ 878 GET_CURRENT_CONTEXT(ctx); 879 880 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 881 882 if (m == NULL) { 883 _mesa_error(ctx, GL_INVALID_VALUE, 884 "glBeginPerfMonitorAMD(invalid monitor)"); 885 return; 886 } 887 888 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is 889 * called when a performance monitor is already active." 890 */ 891 if (m->Active) { 892 _mesa_error(ctx, GL_INVALID_OPERATION, 893 "glBeginPerfMonitor(already active)"); 894 return; 895 } 896 897 /* The driver is free to return false if it can't begin monitoring for 898 * any reason. This translates into an INVALID_OPERATION error. 899 */ 900 if (begin_perf_monitor(ctx, m)) { 901 m->Active = true; 902 m->Ended = false; 903 } else { 904 _mesa_error(ctx, GL_INVALID_OPERATION, 905 "glBeginPerfMonitor(driver unable to begin monitoring)"); 906 } 907} 908 909void GLAPIENTRY 910_mesa_EndPerfMonitorAMD(GLuint monitor) 911{ 912 GET_CURRENT_CONTEXT(ctx); 913 914 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 915 916 if (m == NULL) { 917 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)"); 918 return; 919 } 920 921 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called 922 * when a performance monitor is not currently started." 923 */ 924 if (!m->Active) { 925 _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfMonitor(not active)"); 926 return; 927 } 928 929 end_perf_monitor(ctx, m); 930 931 m->Active = false; 932 m->Ended = true; 933} 934 935/** 936 * Return the number of bytes needed to store a monitor's result. 937 */ 938static unsigned 939perf_monitor_result_size(const struct gl_context *ctx, 940 const struct gl_perf_monitor_object *m) 941{ 942 unsigned group, counter; 943 unsigned size = 0; 944 945 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) { 946 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group]; 947 948 BITSET_FOREACH_SET(counter, m->ActiveCounters[group], g->NumCounters) { 949 const struct gl_perf_monitor_counter *c = &g->Counters[counter]; 950 951 size += sizeof(uint32_t); /* Group ID */ 952 size += sizeof(uint32_t); /* Counter ID */ 953 size += _mesa_perf_monitor_counter_size(c); 954 } 955 } 956 return size; 957} 958 959void GLAPIENTRY 960_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname, 961 GLsizei dataSize, GLuint *data, 962 GLint *bytesWritten) 963{ 964 GET_CURRENT_CONTEXT(ctx); 965 966 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 967 bool result_available; 968 969 if (m == NULL) { 970 _mesa_error(ctx, GL_INVALID_VALUE, 971 "glGetPerfMonitorCounterDataAMD(invalid monitor)"); 972 return; 973 } 974 975 /* "It is an INVALID_OPERATION error for <data> to be NULL." */ 976 if (data == NULL) { 977 _mesa_error(ctx, GL_INVALID_OPERATION, 978 "glGetPerfMonitorCounterDataAMD(data == NULL)"); 979 return; 980 } 981 982 /* We need at least enough room for a single value. */ 983 if (dataSize < sizeof(GLuint)) { 984 if (bytesWritten != NULL) 985 *bytesWritten = 0; 986 return; 987 } 988 989 /* If the monitor has never ended, there is no result. */ 990 result_available = m->Ended && 991 is_perf_monitor_result_available(ctx, m); 992 993 /* AMD appears to return 0 for all queries unless a result is available. */ 994 if (!result_available) { 995 *data = 0; 996 if (bytesWritten != NULL) 997 *bytesWritten = sizeof(GLuint); 998 return; 999 } 1000 1001 switch (pname) { 1002 case GL_PERFMON_RESULT_AVAILABLE_AMD: 1003 *data = 1; 1004 if (bytesWritten != NULL) 1005 *bytesWritten = sizeof(GLuint); 1006 break; 1007 case GL_PERFMON_RESULT_SIZE_AMD: 1008 *data = perf_monitor_result_size(ctx, m); 1009 if (bytesWritten != NULL) 1010 *bytesWritten = sizeof(GLuint); 1011 break; 1012 case GL_PERFMON_RESULT_AMD: 1013 get_perf_monitor_result(ctx, m, dataSize, data, bytesWritten); 1014 break; 1015 default: 1016 _mesa_error(ctx, GL_INVALID_ENUM, 1017 "glGetPerfMonitorCounterDataAMD(pname)"); 1018 } 1019} 1020 1021/** 1022 * Returns how many bytes a counter's value takes up. 1023 */ 1024unsigned 1025_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c) 1026{ 1027 switch (c->Type) { 1028 case GL_FLOAT: 1029 case GL_PERCENTAGE_AMD: 1030 return sizeof(GLfloat); 1031 case GL_UNSIGNED_INT: 1032 return sizeof(GLuint); 1033 case GL_UNSIGNED_INT64_AMD: 1034 return sizeof(uint64_t); 1035 default: 1036 assert(!"Should not get here: invalid counter type"); 1037 return 0; 1038 } 1039} 1040