summaryrefslogtreecommitdiffstats
path: root/src/mesa/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/main')
-rw-r--r--src/mesa/main/context.c2
-rw-r--r--src/mesa/main/dd.h24
-rw-r--r--src/mesa/main/extensions.c1
-rw-r--r--src/mesa/main/mtypes.h86
-rw-r--r--src/mesa/main/performance_monitor.c609
-rw-r--r--src/mesa/main/performance_monitor.h85
-rw-r--r--src/mesa/main/tests/dispatch_sanity.cpp13
7 files changed, 820 insertions, 0 deletions
diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
index 58f42cc5b23..310518c92f4 100644
--- a/src/mesa/main/context.c
+++ b/src/mesa/main/context.c
@@ -105,6 +105,7 @@
#include "macros.h"
#include "matrix.h"
#include "multisample.h"
+#include "performance_monitor.h"
#include "pixel.h"
#include "pixelstore.h"
#include "points.h"
@@ -764,6 +765,7 @@ init_attrib_groups(struct gl_context *ctx)
_mesa_init_lighting( ctx );
_mesa_init_matrix( ctx );
_mesa_init_multisample( ctx );
+ _mesa_init_performance_monitors( ctx );
_mesa_init_pixel( ctx );
_mesa_init_pixelstore( ctx );
_mesa_init_point( ctx );
diff --git a/src/mesa/main/dd.h b/src/mesa/main/dd.h
index c1d9b2c9585..0806e41a021 100644
--- a/src/mesa/main/dd.h
+++ b/src/mesa/main/dd.h
@@ -645,6 +645,30 @@ struct dd_function_table {
void (*WaitQuery)(struct gl_context *ctx, struct gl_query_object *q);
/*@}*/
+ /**
+ * \name Performance monitors
+ */
+ /*@{*/
+ struct gl_perf_monitor_object * (*NewPerfMonitor)(struct gl_context *ctx);
+ void (*DeletePerfMonitor)(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m);
+ GLboolean (*BeginPerfMonitor)(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m);
+
+ /** Stop an active performance monitor, discarding results. */
+ void (*ResetPerfMonitor)(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m);
+ void (*EndPerfMonitor)(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m);
+ GLboolean (*IsPerfMonitorResultAvailable)(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m);
+ void (*GetPerfMonitorResult)(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m,
+ GLsizei dataSize,
+ GLuint *data,
+ GLint *bytesWritten);
+ /*@}*/
+
/**
* \name Vertex Array objects
diff --git a/src/mesa/main/extensions.c b/src/mesa/main/extensions.c
index 34615e3e0bc..eb936205962 100644
--- a/src/mesa/main/extensions.c
+++ b/src/mesa/main/extensions.c
@@ -291,6 +291,7 @@ static const struct extension extension_table[] = {
{ "GL_3DFX_texture_compression_FXT1", o(TDFX_texture_compression_FXT1), GL, 1999 },
{ "GL_AMD_conservative_depth", o(ARB_conservative_depth), GL, 2009 },
{ "GL_AMD_draw_buffers_blend", o(ARB_draw_buffers_blend), GL, 2009 },
+ { "GL_AMD_performance_monitor", o(AMD_performance_monitor), GL, 2007 },
{ "GL_AMD_seamless_cubemap_per_texture", o(AMD_seamless_cubemap_per_texture), GL, 2009 },
{ "GL_AMD_shader_stencil_export", o(ARB_shader_stencil_export), GL, 2009 },
{ "GL_AMD_vertex_shader_layer", o(AMD_vertex_shader_layer), GL, 2012 },
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index 448946c0334..dc1b902b8a7 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -1778,6 +1778,89 @@ struct gl_transform_feedback_state
/**
+ * A "performance monitor" as described in AMD_performance_monitor.
+ */
+struct gl_perf_monitor_object
+{
+ GLboolean Active;
+
+ /**
+ * A list of groups with currently active counters.
+ *
+ * ActiveGroups[g] == n if there are n counters active from group 'g'.
+ */
+ unsigned *ActiveGroups;
+
+ /**
+ * An array of bitsets, subscripted by group ID, then indexed by counter ID.
+ *
+ * Checking whether counter 'c' in group 'g' is active can be done via:
+ *
+ * BITSET_TEST(ActiveCounters[g], c)
+ */
+ GLuint **ActiveCounters;
+};
+
+
+union gl_perf_monitor_counter_value
+{
+ float f;
+ uint64_t u64;
+ uint32_t u32;
+};
+
+
+struct gl_perf_monitor_counter
+{
+ /** Human readable name for the counter. */
+ const char *Name;
+
+ /**
+ * Data type of the counter. Valid values are FLOAT, UNSIGNED_INT,
+ * UNSIGNED_INT64_AMD, and PERCENTAGE_AMD.
+ */
+ GLenum Type;
+
+ /** Minimum counter value. */
+ union gl_perf_monitor_counter_value Minimum;
+
+ /** Maximum counter value. */
+ union gl_perf_monitor_counter_value Maximum;
+};
+
+
+struct gl_perf_monitor_group
+{
+ /** Human readable name for the group. */
+ const char *Name;
+
+ /**
+ * Maximum number of counters in this group which can be active at the
+ * same time.
+ */
+ GLuint MaxActiveCounters;
+
+ /** Array of counters within this group. */
+ const struct gl_perf_monitor_counter *Counters;
+ GLuint NumCounters;
+};
+
+
+/**
+ * Context state for AMD_performance_monitor.
+ */
+struct gl_perf_monitor_state
+{
+ /** Array of performance monitor groups (indexed by group ID) */
+ const struct gl_perf_monitor_group *Groups;
+ GLuint NumGroups;
+
+ /** The table of all performance monitors. */
+ struct _mesa_HashTable *Monitors;
+};
+
+
+/**
* Names of the various vertex/fragment program register files, etc.
*
* NOTE: first four tokens must fit into 2 bits (see t_vb_arbprogram.c)
@@ -3176,6 +3259,7 @@ struct gl_extensions
GLboolean EXT_vertex_array_bgra;
GLboolean OES_standard_derivatives;
/* vendor extensions */
+ GLboolean AMD_performance_monitor;
GLboolean AMD_seamless_cubemap_per_texture;
GLboolean AMD_vertex_shader_layer;
GLboolean APPLE_object_purgeable;
@@ -3639,6 +3723,8 @@ struct gl_context
struct gl_transform_feedback_state TransformFeedback;
+ struct gl_perf_monitor_state PerfMonitor;
+
struct gl_buffer_object *CopyReadBuffer; /**< GL_ARB_copy_buffer */
struct gl_buffer_object *CopyWriteBuffer; /**< GL_ARB_copy_buffer */
diff --git a/src/mesa/main/performance_monitor.c b/src/mesa/main/performance_monitor.c
new file mode 100644
index 00000000000..8dfa8261e34
--- /dev/null
+++ b/src/mesa/main/performance_monitor.c
@@ -0,0 +1,609 @@
+/*
+ * 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 "bitset.h"
+#include "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 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->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 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);
+
+ if (numGroups != NULL)
+ *numGroups = ctx->PerfMonitor.NumGroups;
+
+ if (groupsSize > 0 && groups != NULL) {
+ unsigned i;
+ unsigned n = MIN2(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 = 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, 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 = 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;
+
+ 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;
+
+ 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)
+{
+ GLuint first;
+ GET_CURRENT_CONTEXT(ctx);
+
+ if (MESA_VERBOSE & VERBOSE_API)
+ _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
+
+ if (n < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
+ return;
+ }
+
+ if (monitors == NULL)
+ return;
+
+ /* We don't actually need them to be contiguous, but this is what
+ * the rest of Mesa does, so we may as well.
+ */
+ first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
+ if (first) {
+ GLsizei i;
+ for (i = 0; i < n; i++) {
+ struct gl_perf_monitor_object *m =
+ new_performance_monitor(ctx, first + i);
+ if (!m) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
+ return;
+ }
+ monitors[i] = first + i;
+ _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
+ }
+ } 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);
+
+ _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);
+ unsigned 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++) {
+ ++m->ActiveGroups[group];
+ BITSET_SET(m->ActiveCounters[group], counterList[i]);
+ }
+ } else {
+ /* Disable the counters */
+ for (i = 0; i < numCounters; 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;
+ } 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, "glBeginPerfMonitor(not active)");
+ return;
+ }
+
+ ctx->Driver.EndPerfMonitor(ctx, m);
+
+ m->Active = false;
+}
+
+/**
+ * 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];
+ for (counter = 0; counter < g->NumCounters; counter++) {
+ const struct gl_perf_monitor_counter *c = &g->Counters[counter];
+
+ if (!BITSET_TEST(m->ActiveCounters[group], counter))
+ continue;
+
+ 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);
+
+ 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;
+ }
+
+ /* AMD appears to return 0 for all queries unless a result is available. */
+ if (!ctx->Driver.IsPerfMonitorResultAvailable(ctx, m)) {
+ *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;
+ }
+}
diff --git a/src/mesa/main/performance_monitor.h b/src/mesa/main/performance_monitor.h
new file mode 100644
index 00000000000..a852a4184c2
--- /dev/null
+++ b/src/mesa/main/performance_monitor.h
@@ -0,0 +1,85 @@
+/*
+ * 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.h
+ * Core Mesa support for the AMD_performance_monitor extension.
+ */
+
+#pragma once
+#ifndef PERFORMANCE_MONITOR_H
+#define PERFORMANCE_MONITOR_H
+
+#include "glheader.h"
+
+extern void
+_mesa_init_performance_monitors(struct gl_context *ctx);
+
+extern void GLAPIENTRY
+_mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
+ GLuint *groups);
+
+extern void GLAPIENTRY
+_mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
+ GLint *maxActiveCounters,
+ GLsizei countersSize, GLuint *counters);
+
+extern void GLAPIENTRY
+_mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
+ GLsizei *length, GLchar *groupString);
+
+extern void GLAPIENTRY
+_mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
+ GLsizei bufSize, GLsizei *length,
+ GLchar *counterString);
+
+extern void GLAPIENTRY
+_mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
+ GLvoid *data);
+
+extern void GLAPIENTRY
+_mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors);
+
+extern void GLAPIENTRY
+_mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors);
+
+extern void GLAPIENTRY
+_mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
+ GLuint group, GLint numCounters,
+ GLuint *counterList);
+
+extern void GLAPIENTRY
+_mesa_BeginPerfMonitorAMD(GLuint monitor);
+
+extern void GLAPIENTRY
+_mesa_EndPerfMonitorAMD(GLuint monitor);
+
+extern void GLAPIENTRY
+_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
+ GLsizei dataSize, GLuint *data,
+ GLint *bytesWritten);
+
+unsigned
+_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *);
+
+#endif
diff --git a/src/mesa/main/tests/dispatch_sanity.cpp b/src/mesa/main/tests/dispatch_sanity.cpp
index bea6e964b41..244173af87c 100644
--- a/src/mesa/main/tests/dispatch_sanity.cpp
+++ b/src/mesa/main/tests/dispatch_sanity.cpp
@@ -908,6 +908,19 @@ const struct function gl_core_functions_possible[] = {
{ "glObjectLabel", 11, -1 },
{ "glObjectPtrLabel", 11, -1 },
+ /* GL_AMD_performance_monitor */
+ { "glGetPerfMonitorGroupsAMD", 11, -1 },
+ { "glGetPerfMonitorCountersAMD", 11, -1 },
+ { "glGetPerfMonitorGroupStringAMD", 11, -1 },
+ { "glGetPerfMonitorCounterStringAMD", 11, -1 },
+ { "glGetPerfMonitorCounterInfoAMD", 11, -1 },
+ { "glGenPerfMonitorsAMD", 11, -1 },
+ { "glDeletePerfMonitorsAMD", 11, -1 },
+ { "glSelectPerfMonitorCountersAMD", 11, -1 },
+ { "glBeginPerfMonitorAMD", 11, -1 },
+ { "glEndPerfMonitorAMD", 11, -1 },
+ { "glGetPerfMonitorCounterDataAMD", 11, -1 },
+
{ NULL, 0, -1 }
};