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 48void 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 56static inline void 57init_groups(struct gl_context *ctx) 58{ 59 if (unlikely(!ctx->PerfMonitor.Groups)) 60 ctx->Driver.InitPerfMonitorGroups(ctx); 61} 62 63static struct gl_perf_monitor_object * 64new_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 96fail: 97 ralloc_free(m->ActiveGroups); 98 ralloc_free(m->ActiveCounters); 99 ctx->Driver.DeletePerfMonitor(ctx, m); 100 return NULL; 101} 102 103static void 104free_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 114void 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 122static inline struct gl_perf_monitor_object * 123lookup_monitor(struct gl_context *ctx, GLuint id) 124{ 125 return (struct gl_perf_monitor_object *) 126 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id); 127} 128 129static inline const struct gl_perf_monitor_group * 130get_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 138static inline const struct gl_perf_monitor_counter * 139get_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/*****************************************************************************/ 148 149void GLAPIENTRY 150_mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize, 151 GLuint *groups) 152{ 153 GET_CURRENT_CONTEXT(ctx); 154 init_groups(ctx); 155 156 if (numGroups != NULL) 157 *numGroups = ctx->PerfMonitor.NumGroups; 158 159 if (groupsSize > 0 && groups != NULL) { 160 unsigned i; 161 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups); 162 163 /* We just use the index in the Groups array as the ID. */ 164 for (i = 0; i < n; i++) 165 groups[i] = i; 166 } 167} 168 169void GLAPIENTRY 170_mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters, 171 GLint *maxActiveCounters, 172 GLsizei countersSize, GLuint *counters) 173{ 174 GET_CURRENT_CONTEXT(ctx); 175 const struct gl_perf_monitor_group *group_obj; 176 177 init_groups(ctx); 178 179 group_obj = get_group(ctx, group); 180 if (group_obj == NULL) { 181 _mesa_error(ctx, GL_INVALID_VALUE, 182 "glGetPerfMonitorCountersAMD(invalid group)"); 183 return; 184 } 185 186 if (maxActiveCounters != NULL) 187 *maxActiveCounters = group_obj->MaxActiveCounters; 188 189 if (numCounters != NULL) 190 *numCounters = group_obj->NumCounters; 191 192 if (counters != NULL) { 193 unsigned i; 194 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize); 195 for (i = 0; i < n; i++) { 196 /* We just use the index in the Counters array as the ID. */ 197 counters[i] = i; 198 } 199 } 200} 201 202void GLAPIENTRY 203_mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize, 204 GLsizei *length, GLchar *groupString) 205{ 206 GET_CURRENT_CONTEXT(ctx); 207 const struct gl_perf_monitor_group *group_obj; 208 209 init_groups(ctx); 210 211 group_obj = get_group(ctx, group); 212 if (group_obj == NULL) { 213 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD"); 214 return; 215 } 216 217 if (bufSize == 0) { 218 /* Return the number of characters that would be required to hold the 219 * group string, excluding the null terminator. 220 */ 221 if (length != NULL) 222 *length = strlen(group_obj->Name); 223 } else { 224 if (length != NULL) 225 *length = MIN2(strlen(group_obj->Name), bufSize); 226 if (groupString != NULL) 227 strncpy(groupString, group_obj->Name, bufSize); 228 } 229} 230 231void GLAPIENTRY 232_mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter, 233 GLsizei bufSize, GLsizei *length, 234 GLchar *counterString) 235{ 236 GET_CURRENT_CONTEXT(ctx); 237 238 const struct gl_perf_monitor_group *group_obj; 239 const struct gl_perf_monitor_counter *counter_obj; 240 241 init_groups(ctx); 242 243 group_obj = get_group(ctx, group); 244 245 if (group_obj == NULL) { 246 _mesa_error(ctx, GL_INVALID_VALUE, 247 "glGetPerfMonitorCounterStringAMD(invalid group)"); 248 return; 249 } 250 251 counter_obj = get_counter(group_obj, counter); 252 253 if (counter_obj == NULL) { 254 _mesa_error(ctx, GL_INVALID_VALUE, 255 "glGetPerfMonitorCounterStringAMD(invalid counter)"); 256 return; 257 } 258 259 if (bufSize == 0) { 260 /* Return the number of characters that would be required to hold the 261 * counter string, excluding the null terminator. 262 */ 263 if (length != NULL) 264 *length = strlen(counter_obj->Name); 265 } else { 266 if (length != NULL) 267 *length = MIN2(strlen(counter_obj->Name), bufSize); 268 if (counterString != NULL) 269 strncpy(counterString, counter_obj->Name, bufSize); 270 } 271} 272 273void GLAPIENTRY 274_mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname, 275 GLvoid *data) 276{ 277 GET_CURRENT_CONTEXT(ctx); 278 279 const struct gl_perf_monitor_group *group_obj; 280 const struct gl_perf_monitor_counter *counter_obj; 281 282 init_groups(ctx); 283 284 group_obj = get_group(ctx, group); 285 286 if (group_obj == NULL) { 287 _mesa_error(ctx, GL_INVALID_VALUE, 288 "glGetPerfMonitorCounterInfoAMD(invalid group)"); 289 return; 290 } 291 292 counter_obj = get_counter(group_obj, counter); 293 294 if (counter_obj == NULL) { 295 _mesa_error(ctx, GL_INVALID_VALUE, 296 "glGetPerfMonitorCounterInfoAMD(invalid counter)"); 297 return; 298 } 299 300 switch (pname) { 301 case GL_COUNTER_TYPE_AMD: 302 *((GLenum *) data) = counter_obj->Type; 303 break; 304 305 case GL_COUNTER_RANGE_AMD: 306 switch (counter_obj->Type) { 307 case GL_FLOAT: 308 case GL_PERCENTAGE_AMD: { 309 float *f_data = data; 310 f_data[0] = counter_obj->Minimum.f; 311 f_data[1] = counter_obj->Maximum.f; 312 break; 313 } 314 case GL_UNSIGNED_INT: { 315 uint32_t *u32_data = data; 316 u32_data[0] = counter_obj->Minimum.u32; 317 u32_data[1] = counter_obj->Maximum.u32; 318 break; 319 } 320 case GL_UNSIGNED_INT64_AMD: { 321 uint64_t *u64_data = data; 322 u64_data[0] = counter_obj->Minimum.u64; 323 u64_data[1] = counter_obj->Maximum.u64; 324 break; 325 } 326 default: 327 assert(!"Should not get here: invalid counter type"); 328 } 329 break; 330 331 default: 332 _mesa_error(ctx, GL_INVALID_ENUM, 333 "glGetPerfMonitorCounterInfoAMD(pname)"); 334 return; 335 } 336} 337 338void GLAPIENTRY 339_mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors) 340{ 341 GLuint first; 342 GET_CURRENT_CONTEXT(ctx); 343 344 if (MESA_VERBOSE & VERBOSE_API) 345 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n); 346 347 init_groups(ctx); 348 349 if (n < 0) { 350 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)"); 351 return; 352 } 353 354 if (monitors == NULL) 355 return; 356 357 /* We don't actually need them to be contiguous, but this is what 358 * the rest of Mesa does, so we may as well. 359 */ 360 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n); 361 if (first) { 362 GLsizei i; 363 for (i = 0; i < n; i++) { 364 struct gl_perf_monitor_object *m = 365 new_performance_monitor(ctx, first + i); 366 if (!m) { 367 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); 368 return; 369 } 370 monitors[i] = first + i; 371 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m); 372 } 373 } else { 374 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); 375 return; 376 } 377} 378 379void GLAPIENTRY 380_mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors) 381{ 382 GLint i; 383 GET_CURRENT_CONTEXT(ctx); 384 385 if (MESA_VERBOSE & VERBOSE_API) 386 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n); 387 388 if (n < 0) { 389 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)"); 390 return; 391 } 392 393 if (monitors == NULL) 394 return; 395 396 for (i = 0; i < n; i++) { 397 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]); 398 399 if (m) { 400 /* Give the driver a chance to stop the monitor if it's active. */ 401 if (m->Active) { 402 ctx->Driver.ResetPerfMonitor(ctx, m); 403 m->Ended = false; 404 } 405 406 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]); 407 ralloc_free(m->ActiveGroups); 408 ralloc_free(m->ActiveCounters); 409 ctx->Driver.DeletePerfMonitor(ctx, m); 410 } else { 411 /* "INVALID_VALUE error will be generated if any of the monitor IDs 412 * in the <monitors> parameter to DeletePerfMonitorsAMD do not 413 * reference a valid generated monitor ID." 414 */ 415 _mesa_error(ctx, GL_INVALID_VALUE, 416 "glDeletePerfMonitorsAMD(invalid monitor)"); 417 } 418 } 419} 420 421void GLAPIENTRY 422_mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable, 423 GLuint group, GLint numCounters, 424 GLuint *counterList) 425{ 426 GET_CURRENT_CONTEXT(ctx); 427 int i; 428 struct gl_perf_monitor_object *m; 429 const struct gl_perf_monitor_group *group_obj; 430 431 m = lookup_monitor(ctx, monitor); 432 433 /* "INVALID_VALUE error will be generated if the <monitor> parameter to 434 * SelectPerfMonitorCountersAMD does not reference a monitor created by 435 * GenPerfMonitorsAMD." 436 */ 437 if (m == NULL) { 438 _mesa_error(ctx, GL_INVALID_VALUE, 439 "glSelectPerfMonitorCountersAMD(invalid monitor)"); 440 return; 441 } 442 443 group_obj = get_group(ctx, group); 444 445 /* "INVALID_VALUE error will be generated if the <group> parameter to 446 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD, 447 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or 448 * SelectPerfMonitorCountersAMD does not reference a valid group ID." 449 */ 450 if (group_obj == NULL) { 451 _mesa_error(ctx, GL_INVALID_VALUE, 452 "glSelectPerfMonitorCountersAMD(invalid group)"); 453 return; 454 } 455 456 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to 457 * SelectPerfMonitorCountersAMD is less than 0." 458 */ 459 if (numCounters < 0) { 460 _mesa_error(ctx, GL_INVALID_VALUE, 461 "glSelectPerfMonitorCountersAMD(numCounters < 0)"); 462 return; 463 } 464 465 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding 466 * results for that monitor become invalidated and the result queries 467 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0." 468 */ 469 ctx->Driver.ResetPerfMonitor(ctx, m); 470 471 /* Sanity check the counter ID list. */ 472 for (i = 0; i < numCounters; i++) { 473 if (counterList[i] >= group_obj->NumCounters) { 474 _mesa_error(ctx, GL_INVALID_VALUE, 475 "glSelectPerfMonitorCountersAMD(invalid counter ID)"); 476 return; 477 } 478 } 479 480 if (enable) { 481 /* Enable the counters */ 482 for (i = 0; i < numCounters; i++) { 483 if (!BITSET_TEST(m->ActiveCounters[group], counterList[i])) { 484 ++m->ActiveGroups[group]; 485 BITSET_SET(m->ActiveCounters[group], counterList[i]); 486 } 487 } 488 } else { 489 /* Disable the counters */ 490 for (i = 0; i < numCounters; i++) { 491 if (BITSET_TEST(m->ActiveCounters[group], counterList[i])) { 492 --m->ActiveGroups[group]; 493 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]); 494 } 495 } 496 } 497} 498 499void GLAPIENTRY 500_mesa_BeginPerfMonitorAMD(GLuint monitor) 501{ 502 GET_CURRENT_CONTEXT(ctx); 503 504 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 505 506 if (m == NULL) { 507 _mesa_error(ctx, GL_INVALID_VALUE, 508 "glBeginPerfMonitorAMD(invalid monitor)"); 509 return; 510 } 511 512 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is 513 * called when a performance monitor is already active." 514 */ 515 if (m->Active) { 516 _mesa_error(ctx, GL_INVALID_OPERATION, 517 "glBeginPerfMonitor(already active)"); 518 return; 519 } 520 521 /* The driver is free to return false if it can't begin monitoring for 522 * any reason. This translates into an INVALID_OPERATION error. 523 */ 524 if (ctx->Driver.BeginPerfMonitor(ctx, m)) { 525 m->Active = true; 526 m->Ended = false; 527 } else { 528 _mesa_error(ctx, GL_INVALID_OPERATION, 529 "glBeginPerfMonitor(driver unable to begin monitoring)"); 530 } 531} 532 533void GLAPIENTRY 534_mesa_EndPerfMonitorAMD(GLuint monitor) 535{ 536 GET_CURRENT_CONTEXT(ctx); 537 538 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 539 540 if (m == NULL) { 541 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)"); 542 return; 543 } 544 545 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called 546 * when a performance monitor is not currently started." 547 */ 548 if (!m->Active) { 549 _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfMonitor(not active)"); 550 return; 551 } 552 553 ctx->Driver.EndPerfMonitor(ctx, m); 554 555 m->Active = false; 556 m->Ended = true; 557} 558 559/** 560 * Return the number of bytes needed to store a monitor's result. 561 */ 562static unsigned 563perf_monitor_result_size(const struct gl_context *ctx, 564 const struct gl_perf_monitor_object *m) 565{ 566 unsigned group, counter; 567 unsigned size = 0; 568 569 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) { 570 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group]; 571 BITSET_WORD tmp; 572 573 BITSET_FOREACH_SET(counter, tmp, m->ActiveCounters[group], g->NumCounters) { 574 const struct gl_perf_monitor_counter *c = &g->Counters[counter]; 575 576 size += sizeof(uint32_t); /* Group ID */ 577 size += sizeof(uint32_t); /* Counter ID */ 578 size += _mesa_perf_monitor_counter_size(c); 579 } 580 } 581 return size; 582} 583 584void GLAPIENTRY 585_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname, 586 GLsizei dataSize, GLuint *data, 587 GLint *bytesWritten) 588{ 589 GET_CURRENT_CONTEXT(ctx); 590 591 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); 592 bool result_available; 593 594 if (m == NULL) { 595 _mesa_error(ctx, GL_INVALID_VALUE, 596 "glGetPerfMonitorCounterDataAMD(invalid monitor)"); 597 return; 598 } 599 600 /* "It is an INVALID_OPERATION error for <data> to be NULL." */ 601 if (data == NULL) { 602 _mesa_error(ctx, GL_INVALID_OPERATION, 603 "glGetPerfMonitorCounterDataAMD(data == NULL)"); 604 return; 605 } 606 607 /* We need at least enough room for a single value. */ 608 if (dataSize < sizeof(GLuint)) { 609 if (bytesWritten != NULL) 610 *bytesWritten = 0; 611 return; 612 } 613 614 /* If the monitor has never ended, there is no result. */ 615 result_available = m->Ended && 616 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m); 617 618 /* AMD appears to return 0 for all queries unless a result is available. */ 619 if (!result_available) { 620 *data = 0; 621 if (bytesWritten != NULL) 622 *bytesWritten = sizeof(GLuint); 623 return; 624 } 625 626 switch (pname) { 627 case GL_PERFMON_RESULT_AVAILABLE_AMD: 628 *data = 1; 629 if (bytesWritten != NULL) 630 *bytesWritten = sizeof(GLuint); 631 break; 632 case GL_PERFMON_RESULT_SIZE_AMD: 633 *data = perf_monitor_result_size(ctx, m); 634 if (bytesWritten != NULL) 635 *bytesWritten = sizeof(GLuint); 636 break; 637 case GL_PERFMON_RESULT_AMD: 638 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten); 639 break; 640 default: 641 _mesa_error(ctx, GL_INVALID_ENUM, 642 "glGetPerfMonitorCounterDataAMD(pname)"); 643 } 644} 645 646/** 647 * Returns how many bytes a counter's value takes up. 648 */ 649unsigned 650_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c) 651{ 652 switch (c->Type) { 653 case GL_FLOAT: 654 case GL_PERCENTAGE_AMD: 655 return sizeof(GLfloat); 656 case GL_UNSIGNED_INT: 657 return sizeof(GLuint); 658 case GL_UNSIGNED_INT64_AMD: 659 return sizeof(uint64_t); 660 default: 661 assert(!"Should not get here: invalid counter type"); 662 return 0; 663 } 664} 665