/* * 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 #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" #include "util/u_memory.h" #include "api_exec_decl.h" #include "state_tracker/st_cb_bitmap.h" #include "state_tracker/st_context.h" #include "state_tracker/st_debug.h" #include "pipe/p_context.h" #include "pipe/p_screen.h" void _mesa_init_performance_monitors(struct gl_context *ctx) { ctx->PerfMonitor.Monitors = _mesa_NewHashTable(); ctx->PerfMonitor.NumGroups = 0; ctx->PerfMonitor.Groups = NULL; } static bool init_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) { struct pipe_context *pipe = ctx->pipe; unsigned *batch = NULL; unsigned num_active_counters = 0; unsigned max_batch_counters = 0; unsigned num_batch_counters = 0; int gid, cid; st_flush_bitmap_cache(st_context(ctx)); /* Determine the number of active counters. */ for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) { const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid]; if (m->ActiveGroups[gid] > g->MaxActiveCounters) { /* Maximum number of counters reached. Cannot start the session. */ if (ST_DEBUG & DEBUG_MESA) { debug_printf("Maximum number of counters reached. " "Cannot start the session!\n"); } return false; } num_active_counters += m->ActiveGroups[gid]; if (g->has_batch) max_batch_counters += m->ActiveGroups[gid]; } if (!num_active_counters) return true; m->active_counters = CALLOC(num_active_counters, sizeof(*m->active_counters)); if (!m->active_counters) return false; if (max_batch_counters) { batch = CALLOC(max_batch_counters, sizeof(*batch)); if (!batch) return false; } /* Create a query for each active counter. */ for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) { const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid]; BITSET_FOREACH_SET(cid, m->ActiveCounters[gid], g->NumCounters) { const struct gl_perf_monitor_counter *c = &g->Counters[cid]; struct gl_perf_counter_object *cntr = &m->active_counters[m->num_active_counters]; cntr->id = cid; cntr->group_id = gid; if (c->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) { cntr->batch_index = num_batch_counters; batch[num_batch_counters++] = c->query_type; } else { cntr->query = pipe->create_query(pipe, c->query_type, 0); if (!cntr->query) goto fail; } ++m->num_active_counters; } } /* Create the batch query. */ if (num_batch_counters) { m->batch_query = pipe->create_batch_query(pipe, num_batch_counters, batch); m->batch_result = CALLOC(num_batch_counters, sizeof(m->batch_result->batch[0])); if (!m->batch_query || !m->batch_result) goto fail; } FREE(batch); return true; fail: FREE(batch); return false; } static void do_reset_perf_monitor(struct gl_perf_monitor_object *m, struct pipe_context *pipe) { unsigned i; for (i = 0; i < m->num_active_counters; ++i) { struct pipe_query *query = m->active_counters[i].query; if (query) pipe->destroy_query(pipe, query); } FREE(m->active_counters); m->active_counters = NULL; m->num_active_counters = 0; if (m->batch_query) { pipe->destroy_query(pipe, m->batch_query); m->batch_query = NULL; } FREE(m->batch_result); m->batch_result = NULL; } static void delete_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) { struct pipe_context *pipe = st_context(ctx)->pipe; do_reset_perf_monitor(m, pipe); FREE(m); } static GLboolean begin_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) { struct pipe_context *pipe = st_context(ctx)->pipe; unsigned i; if (!m->num_active_counters) { /* Create a query for each active counter before starting * a new monitoring session. */ if (!init_perf_monitor(ctx, m)) goto fail; } /* Start the query for each active counter. */ for (i = 0; i < m->num_active_counters; ++i) { struct pipe_query *query = m->active_counters[i].query; if (query && !pipe->begin_query(pipe, query)) goto fail; } if (m->batch_query && !pipe->begin_query(pipe, m->batch_query)) goto fail; return true; fail: /* Failed to start the monitoring session. */ do_reset_perf_monitor(m, pipe); return false; } static void end_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) { struct pipe_context *pipe = st_context(ctx)->pipe; unsigned i; /* Stop the query for each active counter. */ for (i = 0; i < m->num_active_counters; ++i) { struct pipe_query *query = m->active_counters[i].query; if (query) pipe->end_query(pipe, query); } if (m->batch_query) pipe->end_query(pipe, m->batch_query); } static void reset_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) { struct pipe_context *pipe = st_context(ctx)->pipe; if (!m->Ended) end_perf_monitor(ctx, m); do_reset_perf_monitor(m, pipe); if (m->Active) begin_perf_monitor(ctx, m); } static GLboolean is_perf_monitor_result_available(struct gl_context *ctx, struct gl_perf_monitor_object *m) { struct pipe_context *pipe = st_context(ctx)->pipe; unsigned i; if (!m->num_active_counters) return false; /* The result of a monitoring session is only available if the query of * each active counter is idle. */ for (i = 0; i < m->num_active_counters; ++i) { struct pipe_query *query = m->active_counters[i].query; union pipe_query_result result; if (query && !pipe->get_query_result(pipe, query, FALSE, &result)) { /* The query is busy. */ return false; } } if (m->batch_query && !pipe->get_query_result(pipe, m->batch_query, FALSE, m->batch_result)) return false; return true; } static void get_perf_monitor_result(struct gl_context *ctx, struct gl_perf_monitor_object *m, GLsizei dataSize, GLuint *data, GLint *bytesWritten) { struct pipe_context *pipe = st_context(ctx)->pipe; unsigned i; /* Copy data to the supplied array (data). * * The output data format is: for each * active counter. The API allows counters to appear in any order. */ GLsizei offset = 0; bool have_batch_query = false; if (m->batch_query) have_batch_query = pipe->get_query_result(pipe, m->batch_query, TRUE, m->batch_result); /* Read query results for each active counter. */ for (i = 0; i < m->num_active_counters; ++i) { struct gl_perf_counter_object *cntr = &m->active_counters[i]; union pipe_query_result result = { 0 }; int gid, cid; GLenum type; cid = cntr->id; gid = cntr->group_id; type = ctx->PerfMonitor.Groups[gid].Counters[cid].Type; if (cntr->query) { if (!pipe->get_query_result(pipe, cntr->query, TRUE, &result)) continue; } else { if (!have_batch_query) continue; result.batch[0] = m->batch_result->batch[cntr->batch_index]; } data[offset++] = gid; data[offset++] = cid; switch (type) { case GL_UNSIGNED_INT64_AMD: memcpy(&data[offset], &result.u64, sizeof(uint64_t)); offset += sizeof(uint64_t) / sizeof(GLuint); break; case GL_UNSIGNED_INT: memcpy(&data[offset], &result.u32, sizeof(uint32_t)); offset += sizeof(uint32_t) / sizeof(GLuint); break; case GL_FLOAT: case GL_PERCENTAGE_AMD: memcpy(&data[offset], &result.f, sizeof(GLfloat)); offset += sizeof(GLfloat) / sizeof(GLuint); break; } } if (bytesWritten) *bytesWritten = offset * sizeof(GLuint); } void _mesa_free_perfomance_monitor_groups(struct gl_context *ctx) { struct gl_perf_monitor_state *perfmon = &ctx->PerfMonitor; int gid; for (gid = 0; gid < perfmon->NumGroups; gid++) { FREE((void *)perfmon->Groups[gid].Counters); } FREE((void *)perfmon->Groups); } static inline void init_groups(struct gl_context *ctx) { if (likely(ctx->PerfMonitor.Groups)) return; struct gl_perf_monitor_state *perfmon = &ctx->PerfMonitor; struct pipe_screen *screen = ctx->pipe->screen; struct gl_perf_monitor_group *groups = NULL; int num_counters, num_groups; int gid, cid; /* Get the number of available queries. */ num_counters = screen->get_driver_query_info(screen, 0, NULL); /* Get the number of available groups. */ num_groups = screen->get_driver_query_group_info(screen, 0, NULL); groups = CALLOC(num_groups, sizeof(*groups)); if (!groups) return; for (gid = 0; gid < num_groups; gid++) { struct gl_perf_monitor_group *g = &groups[perfmon->NumGroups]; struct pipe_driver_query_group_info group_info; struct gl_perf_monitor_counter *counters = NULL; if (!screen->get_driver_query_group_info(screen, gid, &group_info)) continue; g->Name = group_info.name; g->MaxActiveCounters = group_info.max_active_queries; if (group_info.num_queries) counters = CALLOC(group_info.num_queries, sizeof(*counters)); if (!counters) goto fail; g->Counters = counters; for (cid = 0; cid < num_counters; cid++) { struct gl_perf_monitor_counter *c = &counters[g->NumCounters]; struct pipe_driver_query_info info; if (!screen->get_driver_query_info(screen, cid, &info)) continue; if (info.group_id != gid) continue; c->Name = info.name; switch (info.type) { case PIPE_DRIVER_QUERY_TYPE_UINT64: case PIPE_DRIVER_QUERY_TYPE_BYTES: case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS: case PIPE_DRIVER_QUERY_TYPE_HZ: c->Minimum.u64 = 0; c->Maximum.u64 = info.max_value.u64 ? info.max_value.u64 : UINT64_MAX; c->Type = GL_UNSIGNED_INT64_AMD; break; case PIPE_DRIVER_QUERY_TYPE_UINT: c->Minimum.u32 = 0; c->Maximum.u32 = info.max_value.u32 ? info.max_value.u32 : UINT32_MAX; c->Type = GL_UNSIGNED_INT; break; case PIPE_DRIVER_QUERY_TYPE_FLOAT: c->Minimum.f = 0.0; c->Maximum.f = info.max_value.f ? info.max_value.f : FLT_MAX; c->Type = GL_FLOAT; break; case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE: c->Minimum.f = 0.0f; c->Maximum.f = 100.0f; c->Type = GL_PERCENTAGE_AMD; break; default: unreachable("Invalid driver query type!"); } c->query_type = info.query_type; c->flags = info.flags; if (c->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) g->has_batch = true; g->NumCounters++; } perfmon->NumGroups++; } perfmon->Groups = groups; return; fail: for (gid = 0; gid < num_groups; gid++) { FREE((void *)groups[gid].Counters); } FREE(groups); } static struct gl_perf_monitor_object * new_performance_monitor(struct gl_context *ctx, GLuint index) { unsigned i; struct gl_perf_monitor_object *m = CALLOC_STRUCT(gl_perf_monitor_object); 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); delete_perf_monitor(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); delete_perf_monitor(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) { reset_perf_monitor(ctx, m); m->Ended = false; } _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]); ralloc_free(m->ActiveGroups); ralloc_free(m->ActiveCounters); delete_perf_monitor(ctx, m); } else { /* "INVALID_VALUE error will be generated if any of the monitor IDs * in the 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 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 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 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." */ reset_perf_monitor(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 (begin_perf_monitor(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; } end_perf_monitor(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 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 && is_perf_monitor_result_available(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: get_perf_monitor_result(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; } }