| /* |
| * Copyright © 2012 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /** |
| * \file performance_monitor.c |
| * Core Mesa support for the AMD_performance_monitor extension. |
| * |
| * In order to implement this extension, start by defining two enums: |
| * one for Groups, and one for Counters. These will be used as indexes into |
| * arrays, so they should start at 0 and increment from there. |
| * |
| * Counter IDs need to be globally unique. That is, you can't have counter 7 |
| * in group A and counter 7 in group B. A global enum of all available |
| * counters is a convenient way to guarantee this. |
| */ |
| |
| #include <stdbool.h> |
| #include "glheader.h" |
| #include "context.h" |
| #include "enums.h" |
| #include "hash.h" |
| #include "macros.h" |
| #include "mtypes.h" |
| #include "performance_monitor.h" |
| #include "util/bitset.h" |
| #include "util/ralloc.h" |
| |
| void |
| _mesa_init_performance_monitors(struct gl_context *ctx) |
| { |
| ctx->PerfMonitor.Monitors = _mesa_NewHashTable(); |
| ctx->PerfMonitor.NumGroups = 0; |
| ctx->PerfMonitor.Groups = NULL; |
| } |
| |
| static inline void |
| init_groups(struct gl_context *ctx) |
| { |
| if (unlikely(!ctx->PerfMonitor.Groups)) |
| ctx->Driver.InitPerfMonitorGroups(ctx); |
| } |
| |
| static struct gl_perf_monitor_object * |
| new_performance_monitor(struct gl_context *ctx, GLuint index) |
| { |
| unsigned i; |
| struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx); |
| |
| if (m == NULL) |
| return NULL; |
| |
| m->Name = index; |
| |
| m->Active = false; |
| |
| m->ActiveGroups = |
| rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups); |
| |
| m->ActiveCounters = |
| ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups); |
| |
| if (m->ActiveGroups == NULL || m->ActiveCounters == NULL) |
| goto fail; |
| |
| for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) { |
| const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i]; |
| |
| m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD, |
| BITSET_WORDS(g->NumCounters)); |
| if (m->ActiveCounters[i] == NULL) |
| goto fail; |
| } |
| |
| return m; |
| |
| fail: |
| ralloc_free(m->ActiveGroups); |
| ralloc_free(m->ActiveCounters); |
| ctx->Driver.DeletePerfMonitor(ctx, m); |
| return NULL; |
| } |
| |
| static void |
| free_performance_monitor(void *data, void *user) |
| { |
| struct gl_perf_monitor_object *m = data; |
| struct gl_context *ctx = user; |
| |
| ralloc_free(m->ActiveGroups); |
| ralloc_free(m->ActiveCounters); |
| ctx->Driver.DeletePerfMonitor(ctx, m); |
| } |
| |
| void |
| _mesa_free_performance_monitors(struct gl_context *ctx) |
| { |
| _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors, |
| free_performance_monitor, ctx); |
| _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors); |
| } |
| |
| static inline struct gl_perf_monitor_object * |
| lookup_monitor(struct gl_context *ctx, GLuint id) |
| { |
| return (struct gl_perf_monitor_object *) |
| _mesa_HashLookup(ctx->PerfMonitor.Monitors, id); |
| } |
| |
| static inline const struct gl_perf_monitor_group * |
| get_group(const struct gl_context *ctx, GLuint id) |
| { |
| if (id >= ctx->PerfMonitor.NumGroups) |
| return NULL; |
| |
| return &ctx->PerfMonitor.Groups[id]; |
| } |
| |
| static inline const struct gl_perf_monitor_counter * |
| get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id) |
| { |
| if (id >= group_obj->NumCounters) |
| return NULL; |
| |
| return &group_obj->Counters[id]; |
| } |
| |
| /*****************************************************************************/ |
| |
| void GLAPIENTRY |
| _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize, |
| GLuint *groups) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| init_groups(ctx); |
| |
| if (numGroups != NULL) |
| *numGroups = ctx->PerfMonitor.NumGroups; |
| |
| if (groupsSize > 0 && groups != NULL) { |
| unsigned i; |
| unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups); |
| |
| /* We just use the index in the Groups array as the ID. */ |
| for (i = 0; i < n; i++) |
| groups[i] = i; |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters, |
| GLint *maxActiveCounters, |
| GLsizei countersSize, GLuint *counters) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const struct gl_perf_monitor_group *group_obj; |
| |
| init_groups(ctx); |
| |
| group_obj = get_group(ctx, group); |
| if (group_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glGetPerfMonitorCountersAMD(invalid group)"); |
| return; |
| } |
| |
| if (maxActiveCounters != NULL) |
| *maxActiveCounters = group_obj->MaxActiveCounters; |
| |
| if (numCounters != NULL) |
| *numCounters = group_obj->NumCounters; |
| |
| if (counters != NULL) { |
| unsigned i; |
| unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize); |
| for (i = 0; i < n; i++) { |
| /* We just use the index in the Counters array as the ID. */ |
| counters[i] = i; |
| } |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize, |
| GLsizei *length, GLchar *groupString) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const struct gl_perf_monitor_group *group_obj; |
| |
| init_groups(ctx); |
| |
| group_obj = get_group(ctx, group); |
| if (group_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD"); |
| return; |
| } |
| |
| if (bufSize == 0) { |
| /* Return the number of characters that would be required to hold the |
| * group string, excluding the null terminator. |
| */ |
| if (length != NULL) |
| *length = strlen(group_obj->Name); |
| } else { |
| if (length != NULL) |
| *length = MIN2(strlen(group_obj->Name), bufSize); |
| if (groupString != NULL) |
| strncpy(groupString, group_obj->Name, bufSize); |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter, |
| GLsizei bufSize, GLsizei *length, |
| GLchar *counterString) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| const struct gl_perf_monitor_group *group_obj; |
| const struct gl_perf_monitor_counter *counter_obj; |
| |
| init_groups(ctx); |
| |
| group_obj = get_group(ctx, group); |
| |
| if (group_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glGetPerfMonitorCounterStringAMD(invalid group)"); |
| return; |
| } |
| |
| counter_obj = get_counter(group_obj, counter); |
| |
| if (counter_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glGetPerfMonitorCounterStringAMD(invalid counter)"); |
| return; |
| } |
| |
| if (bufSize == 0) { |
| /* Return the number of characters that would be required to hold the |
| * counter string, excluding the null terminator. |
| */ |
| if (length != NULL) |
| *length = strlen(counter_obj->Name); |
| } else { |
| if (length != NULL) |
| *length = MIN2(strlen(counter_obj->Name), bufSize); |
| if (counterString != NULL) |
| strncpy(counterString, counter_obj->Name, bufSize); |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname, |
| GLvoid *data) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| const struct gl_perf_monitor_group *group_obj; |
| const struct gl_perf_monitor_counter *counter_obj; |
| |
| init_groups(ctx); |
| |
| group_obj = get_group(ctx, group); |
| |
| if (group_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glGetPerfMonitorCounterInfoAMD(invalid group)"); |
| return; |
| } |
| |
| counter_obj = get_counter(group_obj, counter); |
| |
| if (counter_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glGetPerfMonitorCounterInfoAMD(invalid counter)"); |
| return; |
| } |
| |
| switch (pname) { |
| case GL_COUNTER_TYPE_AMD: |
| *((GLenum *) data) = counter_obj->Type; |
| break; |
| |
| case GL_COUNTER_RANGE_AMD: |
| switch (counter_obj->Type) { |
| case GL_FLOAT: |
| case GL_PERCENTAGE_AMD: { |
| float *f_data = data; |
| f_data[0] = counter_obj->Minimum.f; |
| f_data[1] = counter_obj->Maximum.f; |
| break; |
| } |
| case GL_UNSIGNED_INT: { |
| uint32_t *u32_data = data; |
| u32_data[0] = counter_obj->Minimum.u32; |
| u32_data[1] = counter_obj->Maximum.u32; |
| break; |
| } |
| case GL_UNSIGNED_INT64_AMD: { |
| uint64_t *u64_data = data; |
| u64_data[0] = counter_obj->Minimum.u64; |
| u64_data[1] = counter_obj->Maximum.u64; |
| break; |
| } |
| default: |
| assert(!"Should not get here: invalid counter type"); |
| } |
| break; |
| |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, |
| "glGetPerfMonitorCounterInfoAMD(pname)"); |
| return; |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n); |
| |
| init_groups(ctx); |
| |
| if (n < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)"); |
| return; |
| } |
| |
| if (monitors == NULL) |
| return; |
| |
| if (_mesa_HashFindFreeKeys(ctx->PerfMonitor.Monitors, monitors, n)) { |
| GLsizei i; |
| for (i = 0; i < n; i++) { |
| struct gl_perf_monitor_object *m = |
| new_performance_monitor(ctx, monitors[i]); |
| if (!m) { |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); |
| return; |
| } |
| _mesa_HashInsert(ctx->PerfMonitor.Monitors, monitors[i], m, true); |
| } |
| } else { |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD"); |
| return; |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors) |
| { |
| GLint i; |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n); |
| |
| if (n < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)"); |
| return; |
| } |
| |
| if (monitors == NULL) |
| return; |
| |
| for (i = 0; i < n; i++) { |
| struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]); |
| |
| if (m) { |
| /* Give the driver a chance to stop the monitor if it's active. */ |
| if (m->Active) { |
| ctx->Driver.ResetPerfMonitor(ctx, m); |
| m->Ended = false; |
| } |
| |
| _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]); |
| ralloc_free(m->ActiveGroups); |
| ralloc_free(m->ActiveCounters); |
| ctx->Driver.DeletePerfMonitor(ctx, m); |
| } else { |
| /* "INVALID_VALUE error will be generated if any of the monitor IDs |
| * in the <monitors> parameter to DeletePerfMonitorsAMD do not |
| * reference a valid generated monitor ID." |
| */ |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glDeletePerfMonitorsAMD(invalid monitor)"); |
| } |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable, |
| GLuint group, GLint numCounters, |
| GLuint *counterList) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| int i; |
| struct gl_perf_monitor_object *m; |
| const struct gl_perf_monitor_group *group_obj; |
| |
| m = lookup_monitor(ctx, monitor); |
| |
| /* "INVALID_VALUE error will be generated if the <monitor> parameter to |
| * SelectPerfMonitorCountersAMD does not reference a monitor created by |
| * GenPerfMonitorsAMD." |
| */ |
| if (m == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glSelectPerfMonitorCountersAMD(invalid monitor)"); |
| return; |
| } |
| |
| group_obj = get_group(ctx, group); |
| |
| /* "INVALID_VALUE error will be generated if the <group> parameter to |
| * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD, |
| * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or |
| * SelectPerfMonitorCountersAMD does not reference a valid group ID." |
| */ |
| if (group_obj == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glSelectPerfMonitorCountersAMD(invalid group)"); |
| return; |
| } |
| |
| /* "INVALID_VALUE error will be generated if the <numCounters> parameter to |
| * SelectPerfMonitorCountersAMD is less than 0." |
| */ |
| if (numCounters < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glSelectPerfMonitorCountersAMD(numCounters < 0)"); |
| return; |
| } |
| |
| /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding |
| * results for that monitor become invalidated and the result queries |
| * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0." |
| */ |
| ctx->Driver.ResetPerfMonitor(ctx, m); |
| |
| /* Sanity check the counter ID list. */ |
| for (i = 0; i < numCounters; i++) { |
| if (counterList[i] >= group_obj->NumCounters) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glSelectPerfMonitorCountersAMD(invalid counter ID)"); |
| return; |
| } |
| } |
| |
| if (enable) { |
| /* Enable the counters */ |
| for (i = 0; i < numCounters; i++) { |
| if (!BITSET_TEST(m->ActiveCounters[group], counterList[i])) { |
| ++m->ActiveGroups[group]; |
| BITSET_SET(m->ActiveCounters[group], counterList[i]); |
| } |
| } |
| } else { |
| /* Disable the counters */ |
| for (i = 0; i < numCounters; i++) { |
| if (BITSET_TEST(m->ActiveCounters[group], counterList[i])) { |
| --m->ActiveGroups[group]; |
| BITSET_CLEAR(m->ActiveCounters[group], counterList[i]); |
| } |
| } |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_BeginPerfMonitorAMD(GLuint monitor) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); |
| |
| if (m == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glBeginPerfMonitorAMD(invalid monitor)"); |
| return; |
| } |
| |
| /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is |
| * called when a performance monitor is already active." |
| */ |
| if (m->Active) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glBeginPerfMonitor(already active)"); |
| return; |
| } |
| |
| /* The driver is free to return false if it can't begin monitoring for |
| * any reason. This translates into an INVALID_OPERATION error. |
| */ |
| if (ctx->Driver.BeginPerfMonitor(ctx, m)) { |
| m->Active = true; |
| m->Ended = false; |
| } else { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glBeginPerfMonitor(driver unable to begin monitoring)"); |
| } |
| } |
| |
| void GLAPIENTRY |
| _mesa_EndPerfMonitorAMD(GLuint monitor) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); |
| |
| if (m == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)"); |
| return; |
| } |
| |
| /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called |
| * when a performance monitor is not currently started." |
| */ |
| if (!m->Active) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfMonitor(not active)"); |
| return; |
| } |
| |
| ctx->Driver.EndPerfMonitor(ctx, m); |
| |
| m->Active = false; |
| m->Ended = true; |
| } |
| |
| /** |
| * Return the number of bytes needed to store a monitor's result. |
| */ |
| static unsigned |
| perf_monitor_result_size(const struct gl_context *ctx, |
| const struct gl_perf_monitor_object *m) |
| { |
| unsigned group, counter; |
| unsigned size = 0; |
| |
| for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) { |
| const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group]; |
| |
| BITSET_FOREACH_SET(counter, m->ActiveCounters[group], g->NumCounters) { |
| const struct gl_perf_monitor_counter *c = &g->Counters[counter]; |
| |
| size += sizeof(uint32_t); /* Group ID */ |
| size += sizeof(uint32_t); /* Counter ID */ |
| size += _mesa_perf_monitor_counter_size(c); |
| } |
| } |
| return size; |
| } |
| |
| void GLAPIENTRY |
| _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname, |
| GLsizei dataSize, GLuint *data, |
| GLint *bytesWritten) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor); |
| bool result_available; |
| |
| if (m == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glGetPerfMonitorCounterDataAMD(invalid monitor)"); |
| return; |
| } |
| |
| /* "It is an INVALID_OPERATION error for <data> to be NULL." */ |
| if (data == NULL) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glGetPerfMonitorCounterDataAMD(data == NULL)"); |
| return; |
| } |
| |
| /* We need at least enough room for a single value. */ |
| if (dataSize < sizeof(GLuint)) { |
| if (bytesWritten != NULL) |
| *bytesWritten = 0; |
| return; |
| } |
| |
| /* If the monitor has never ended, there is no result. */ |
| result_available = m->Ended && |
| ctx->Driver.IsPerfMonitorResultAvailable(ctx, m); |
| |
| /* AMD appears to return 0 for all queries unless a result is available. */ |
| if (!result_available) { |
| *data = 0; |
| if (bytesWritten != NULL) |
| *bytesWritten = sizeof(GLuint); |
| return; |
| } |
| |
| switch (pname) { |
| case GL_PERFMON_RESULT_AVAILABLE_AMD: |
| *data = 1; |
| if (bytesWritten != NULL) |
| *bytesWritten = sizeof(GLuint); |
| break; |
| case GL_PERFMON_RESULT_SIZE_AMD: |
| *data = perf_monitor_result_size(ctx, m); |
| if (bytesWritten != NULL) |
| *bytesWritten = sizeof(GLuint); |
| break; |
| case GL_PERFMON_RESULT_AMD: |
| ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten); |
| break; |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, |
| "glGetPerfMonitorCounterDataAMD(pname)"); |
| } |
| } |
| |
| /** |
| * Returns how many bytes a counter's value takes up. |
| */ |
| unsigned |
| _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c) |
| { |
| switch (c->Type) { |
| case GL_FLOAT: |
| case GL_PERCENTAGE_AMD: |
| return sizeof(GLfloat); |
| case GL_UNSIGNED_INT: |
| return sizeof(GLuint); |
| case GL_UNSIGNED_INT64_AMD: |
| return sizeof(uint64_t); |
| default: |
| assert(!"Should not get here: invalid counter type"); |
| return 0; |
| } |
| } |