[Mesa-dev] [PATCH v4 1/2] mesa: Add core support for the GL_AMD_performance_monitor extension.
Ian Romanick
idr at freedesktop.org
Tue Sep 24 14:17:04 PDT 2013
Series is
Reviewed-by: Ian Romanick <ian.d.romanick at intel.com>
I had one comment (future work) on patch 2.
On 09/23/2013 08:56 PM, Kenneth Graunke wrote:
> This provides an interface for applications (and OpenGL-based tools) to
> access GPU performance counters. Since the exact performance counters
> available vary between vendors and hardware generations, the extension
> provides an API the application can use to get the names, types, and
> minimum/maximum values of all available counters. Counters are also
> organized into groups.
>
> Applications create "performance monitor" objects, select the counters
> they want to track, and Begin/End monitoring, much like OpenGL's query
> API. Multiple monitors can be in flight simultaneously.
>
> v2: Pass ctx to all driver hooks (suggested by Christoph), and attempt
> to fix overallocation of bitsets (caught by Christoph). Incomplete.
>
> v3: Significantly rework core data structures. Store counters in groups
> rather than in a global list. Use their array index in the group's
> counter list as the ID rather than trying to store a globally unique
> counter ID. Use bitsets for active counters within a group, and
> also track which groups are active so that's easy to query.
>
> v4: Remove _mesa_ prefix on static functions; detect out of memory
> conditions in new_performance_monitor(); make BeginPerfMonitor hook
> return a boolean rather than setting m->Active or raising an error.
> Switch to GLuint/unsigned for NumGroups, NumCounters, and
> MaxActiveCounters (which also means switching a bunch of temporary
> variable types). All suggested by Brian Paul. Also, remove
> commented out code at the bottom of the block. Finally, fix the
> dispatch sanity test (noticed by Ian Romanick).
>
> Signed-off-by: Kenneth Graunke <kenneth at whitecape.org>
> Reviewed-by: Brian Paul <brianp at vmware.com> [v3]
> ---
> src/mapi/glapi/gen/AMD_performance_monitor.xml | 87 ++++
> src/mapi/glapi/gen/Makefile.am | 1 +
> src/mapi/glapi/gen/gl_API.xml | 2 +
> src/mapi/glapi/gen/gl_genexec.py | 1 +
> src/mesa/Makefile.sources | 1 +
> src/mesa/SConscript | 1 +
> src/mesa/main/context.c | 2 +
> src/mesa/main/dd.h | 24 +
> src/mesa/main/extensions.c | 1 +
> src/mesa/main/mtypes.h | 86 ++++
> src/mesa/main/performance_monitor.c | 609 +++++++++++++++++++++++++
> src/mesa/main/performance_monitor.h | 85 ++++
> src/mesa/main/tests/dispatch_sanity.cpp | 13 +
> 13 files changed, 913 insertions(+)
> create mode 100644 src/mapi/glapi/gen/AMD_performance_monitor.xml
> create mode 100644 src/mesa/main/performance_monitor.c
> create mode 100644 src/mesa/main/performance_monitor.h
>
> Brian,
>
> Thanks for the feedback! I agreed with pretty much everything, and I think
> I've made all of the requested changes.
>
> diff --git a/src/mapi/glapi/gen/AMD_performance_monitor.xml b/src/mapi/glapi/gen/AMD_performance_monitor.xml
> new file mode 100644
> index 0000000..b96b263
> --- /dev/null
> +++ b/src/mapi/glapi/gen/AMD_performance_monitor.xml
> @@ -0,0 +1,87 @@
> +<?xml version="1.0"?>
> +<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd">
> +
> +<OpenGLAPI>
> +
> +<category name="GL_AMD_performance_monitor" number="360">
> +
> + <function name="GetPerfMonitorGroupsAMD" offset="assign">
> + <param name="numGroups" type="GLint *"/>
> + <param name="groupsSize" type="GLsizei"/>
> + <param name="groups" type="GLuint *"/>
> + </function>
> +
> + <function name="GetPerfMonitorCountersAMD" offset="assign">
> + <param name="group" type="GLuint"/>
> + <param name="numCounters" type="GLint *"/>
> + <param name="maxActiveCounters" type="GLint *"/>
> + <param name="countersSize" type="GLsizei"/>
> + <param name="counters" type="GLuint *"/>
> + </function>
> +
> + <function name="GetPerfMonitorGroupStringAMD" offset="assign">
> + <param name="group" type="GLuint"/>
> + <param name="bufSize" type="GLsizei"/>
> + <param name="length" type="GLsizei *"/>
> + <param name="groupString" type="GLchar *"/>
> + </function>
> +
> + <function name="GetPerfMonitorCounterStringAMD" offset="assign">
> + <param name="group" type="GLuint"/>
> + <param name="counter" type="GLuint"/>
> + <param name="bufSize" type="GLsizei"/>
> + <param name="length" type="GLsizei *"/>
> + <param name="counterString" type="GLchar *"/>
> + </function>
> +
> + <function name="GetPerfMonitorCounterInfoAMD" offset="assign">
> + <param name="group" type="GLuint"/>
> + <param name="counter" type="GLuint"/>
> + <param name="pname" type="GLenum"/>
> + <param name="data" type="GLvoid *"/>
> + </function>
> +
> + <function name="GenPerfMonitorsAMD" offset="assign">
> + <param name="n" type="GLsizei"/>
> + <param name="monitors" type="GLuint *"/>
> + </function>
> +
> + <function name="DeletePerfMonitorsAMD" offset="assign">
> + <param name="n" type="GLsizei"/>
> + <param name="monitors" type="GLuint *"/>
> + </function>
> +
> + <function name="SelectPerfMonitorCountersAMD" offset="assign">
> + <param name="monitor" type="GLuint"/>
> + <param name="enable" type="GLboolean"/>
> + <param name="group" type="GLuint"/>
> + <param name="numCounters" type="GLint"/>
> + <param name="counterList" type="GLuint *"/>
> + </function>
> +
> + <function name="BeginPerfMonitorAMD" offset="assign">
> + <param name="monitor" type="GLuint"/>
> + </function>
> +
> + <function name="EndPerfMonitorAMD" offset="assign">
> + <param name="monitor" type="GLuint"/>
> + </function>
> +
> + <function name="GetPerfMonitorCounterDataAMD" offset="assign">
> + <param name="monitor" type="GLuint"/>
> + <param name="pname" type="GLenum"/>
> + <param name="dataSize" type="GLsizei"/>
> + <param name="data" type="GLuint *"/>
> + <param name="bytesWritten" type="GLint *"/>
> + </function>
> +
> + <enum name="COUNTER_TYPE_AMD" value="0x8BC0"/>
> + <enum name="COUNTER_RANGE_AMD" value="0x8BC1"/>
> + <enum name="UNSIGNED_INT64_AMD" value="0x8BC2"/>
> + <enum name="PECENTAGE_AMD" value="0x8BC3"/>
> + <enum name="PERFMON_RESULT_AVAILABLE_AMD" value="0x8BC4"/>
> + <enum name="PERFMON_RESULT_SIZE_AMD" value="0x8BC5"/>
> + <enum name="PERFMON_RESULT_AMD" value="0x8BC6"/>
> +</category>
> +
> +</OpenGLAPI>
> diff --git a/src/mapi/glapi/gen/Makefile.am b/src/mapi/glapi/gen/Makefile.am
> index d4fbd35..9b9b995 100644
> --- a/src/mapi/glapi/gen/Makefile.am
> +++ b/src/mapi/glapi/gen/Makefile.am
> @@ -115,6 +115,7 @@ API_XML = \
> ARB_texture_storage.xml \
> ARB_vertex_array_object.xml \
> AMD_draw_buffers_blend.xml \
> + AMD_performance_monitor.xml \
> ARB_vertex_type_2_10_10_10_rev.xml \
> APPLE_object_purgeable.xml \
> APPLE_vertex_array_object.xml \
> diff --git a/src/mapi/glapi/gen/gl_API.xml b/src/mapi/glapi/gen/gl_API.xml
> index 71aa9a7..f6511e9 100644
> --- a/src/mapi/glapi/gen/gl_API.xml
> +++ b/src/mapi/glapi/gen/gl_API.xml
> @@ -12890,6 +12890,8 @@
> <enum name="FRAMEBUFFER_SRGB_CAPABLE_EXT" value="0x8DBA"/>
> </category>
>
> +<xi:include href="AMD_performance_monitor.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
> +
> <category name="GL_APPLE_texture_range" number="367">
> <enum name="TEXTURE_STORAGE_HINT_APPLE" count="1" value="0x85BC">
> <size name="TexParameteriv"/>
> diff --git a/src/mapi/glapi/gen/gl_genexec.py b/src/mapi/glapi/gen/gl_genexec.py
> index be82f90..a074c23 100644
> --- a/src/mapi/glapi/gen/gl_genexec.py
> +++ b/src/mapi/glapi/gen/gl_genexec.py
> @@ -82,6 +82,7 @@ header = """/**
> #include "main/matrix.h"
> #include "main/multisample.h"
> #include "main/objectlabel.h"
> +#include "main/performance_monitor.h"
> #include "main/pixel.h"
> #include "main/pixelstore.h"
> #include "main/points.h"
> diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources
> index 122ea8e..ff242b4 100644
> --- a/src/mesa/Makefile.sources
> +++ b/src/mesa/Makefile.sources
> @@ -65,6 +65,7 @@ MAIN_FILES = \
> $(SRCDIR)main/objectlabel.c \
> $(SRCDIR)main/pack.c \
> $(SRCDIR)main/pbo.c \
> + $(SRCDIR)main/performance_monitor.c \
> $(SRCDIR)main/pixel.c \
> $(SRCDIR)main/pixelstore.c \
> $(SRCDIR)main/pixeltransfer.c \
> diff --git a/src/mesa/SConscript b/src/mesa/SConscript
> index 2cdb79c..9b7712f 100644
> --- a/src/mesa/SConscript
> +++ b/src/mesa/SConscript
> @@ -97,6 +97,7 @@ main_sources = [
> 'main/objectlabel.c',
> 'main/pack.c',
> 'main/pbo.c',
> + 'main/performance_monitor.c',
> 'main/pixel.c',
> 'main/pixelstore.c',
> 'main/pixeltransfer.c',
> diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
> index 58f42cc..310518c 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 c1d9b2c..0806e41 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 34615e3..eb93620 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 c88c1c6..59378c7 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;
> @@ -3641,6 +3725,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 0000000..8dfa826
> --- /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 0000000..a852a41
> --- /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 bea6e96..244173a 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 }
> };
>
>
More information about the mesa-dev
mailing list