[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