blob: 35efea520739bd96a63877b5ff20e8706369ff1f [file] [log] [blame]
Name
AMD_performance_monitor
Name Strings
GL_AMD_performance_monitor
Contributors
Dan Ginsburg
Aaftab Munshi
Dave Oldcorn
Maurice Ribble
Jonathan Zarge
Contact
Dan Ginsburg (dan.ginsburg 'at' amd.com)
Status
???
Version
Last Modified Date: 11/29/2007
Number
OpenGL Extension #360
OpenGL ES Extension #50
Dependencies
None
Overview
This extension enables the capture and reporting of performance monitors.
Performance monitors contain groups of counters which hold arbitrary counted
data. Typically, the counters hold information on performance-related
counters in the underlying hardware. The extension is general enough to
allow the implementation to choose which counters to expose and pick the
data type and range of the counters. The extension also allows counting to
start and end on arbitrary boundaries during rendering.
Issues
1. Should this be an EGL or OpenGL/OpenGL ES extension?
Decision - Make this an OpenGL/OpenGL ES extension
Reason - We would like to expose this extension in both OpenGL and
OpenGL ES which makes EGL an unsuitable choice. Further, support for
EGL is not a requirement and there are platforms that support OpenGL ES
but not EGL, making it difficult to make this an EGL extension.
2. Should the API support multipassing?
Decision - No.
Reason - Multipassing should really be left to the application to do.
This makes the API unnecessarily complicated. A major issue is that
depending on which counters are to be sampled, the # of passes and which
counters get selected in each pass can be difficult to determine. It is
much easier to give a list of counters categorized by groups with
specific information on the number of counters that can be selected from
each group.
3. Should we define a 64-bit data type for UNSIGNED_INT64_AMD?
Decision - No.
Reason - While counters can be returned as 64-bit unsigned integers, the
data is passed back to the application inside of a void*. Therefore,
there is no need in this extension to define a 64-bit data type (e.g.,
GLuint64). It will be up the application to declare a native 64-bit
unsigned integer and cast the returned data to that type.
New Procedures and Functions
void GetPerfMonitorGroupsAMD(int *numGroups, sizei groupsSize,
uint *groups)
void GetPerfMonitorCountersAMD(uint group, int *numCounters,
int *maxActiveCounters, sizei countersSize,
uint *counters)
void GetPerfMonitorGroupStringAMD(uint group, sizei bufSize, sizei *length,
char *groupString)
void GetPerfMonitorCounterStringAMD(uint group, uint counter, sizei bufSize,
sizei *length, char *counterString)
void GetPerfMonitorCounterInfoAMD(uint group, uint counter,
enum pname, void *data)
void GenPerfMonitorsAMD(sizei n, uint *monitors)
void DeletePerfMonitorsAMD(sizei n, uint *monitors)
void SelectPerfMonitorCountersAMD(uint monitor, boolean enable,
uint group, int numCounters,
uint *counterList)
void BeginPerfMonitorAMD(uint monitor)
void EndPerfMonitorAMD(uint monitor)
void GetPerfMonitorCounterDataAMD(uint monitor, enum pname, sizei dataSize,
uint *data, int *bytesWritten)
New Tokens
Accepted by the <pame> parameter of GetPerfMonitorCounterInfoAMD
COUNTER_TYPE_AMD 0x8BC0
COUNTER_RANGE_AMD 0x8BC1
Returned as a valid value in <data> parameter of
GetPerfMonitorCounterInfoAMD if <pname> = COUNTER_TYPE_AMD
UNSIGNED_INT 0x1405
FLOAT 0x1406
UNSIGNED_INT64_AMD 0x8BC2
PERCENTAGE_AMD 0x8BC3
Accepted by the <pname> parameter of GetPerfMonitorCounterDataAMD
PERFMON_RESULT_AVAILABLE_AMD 0x8BC4
PERFMON_RESULT_SIZE_AMD 0x8BC5
PERFMON_RESULT_AMD 0x8BC6
Addition to the GL specification
Add a new section called Performance Monitoring
A performance monitor consists of a number of hardware and software counters
that can be sampled by the GPU and reported back to the application.
Performance counters are organized as a single hierarchy where counters are
categorized into groups. Each group has a list of counters that belong to
the counter and can be sampled, and a maximum number of counters that can be
sampled.
The command
void GetPerfMonitorGroupsAMD(int *numGroups, sizei groupsSize,
uint *groups);
returns the number of available groups in <numGroups>, if <numGroups> is
not NULL. If <groupsSize> is not 0 and <groups> is not NULL, then the list
of available groups is returned. The number of entries that will be
returned in <groups> is determined by <groupsSize>. If <groupsSize> is 0,
no information is copied. Each group is identified by a unique unsigned int
identifier.
The command
void GetPerfMonitorCountersAMD(uint group, int *numCounters,
int *maxActiveCounters,
sizei countersSize,
uint *counters);
returns the following information. For each group, it returns the number of
available counters in <numCounters>, the max number of counters that can be
active at any time in <maxActiveCounters>, and the list of counters in
<counters>. The number of entries that can be returned in <counters> is
determined by <countersSize>. If <countersSize> is 0, no information is
copied. Each counter in a group is identified by a unique unsigned int
identifier. If <group> does not reference a valid group ID, an
INVALID_VALUE error is generated.
The command
void GetPerfMonitorGroupStringAMD(uint group, sizei bufSize,
sizei *length, char *groupString)
returns the string that describes the group name identified by <group> in
<groupString>. The actual number of characters written to <groupString>,
excluding the null terminator, is returned in <length>. If <length> is
NULL, then no length is returned. The maximum number of characters that
may be written into <groupString>, including the null terminator, is
specified by <bufSize>. If <bufSize> is 0 and <groupString> is NULL, the
number of characters that would be required to hold the group string,
excluding the null terminator, is returned in <length>. If <group>
does not reference a valid group ID, an INVALID_VALUE error is generated.
The command
void GetPerfMonitorCounterStringAMD(uint group, uint counter,
sizei bufSize, sizei *length,
char *counterString);
returns the string that describes the counter name identified by <group>
and <counter> in <counterString>. The actual number of characters written
to <counterString>, excluding the null terminator, is returned in <length>.
If <length> is NULL, then no length is returned. The maximum number of
characters that may be written into <counterString>, including the null
terminator, is specified by <bufSize>. If <bufSize> is 0 and
<counterString> is NULL, the number of characters that would be required to
hold the counter string, excluding the null terminator, is returned in
<length>. If <group> does not reference a valid group ID, or <counter>
does not reference a valid counter within the group ID, an INVALID_VALUE
error is generated.
The command
void GetPerfMonitorCounterInfoAMD(uint group, uint counter,
enum pname, void *data);
returns the following information about a counter. For a <counter>
belonging to <group>, we can query the counter type and counter range. If
<pname> is COUNTER_TYPE_AMD, then <data> returns the type. Valid type
values returned are UNSIGNED_INT, UNSIGNED_INT64_AMD, PERCENTAGE_AMD, FLOAT.
If type value returned is PERCENTAGE_AMD, then this describes a float
value that is in the range [0.0 .. 100.0]. If <pname> is COUNTER_RANGE_AMD,
<data> returns two values representing a minimum and a maximum. The
counter's type is used to determine the format in which the range values
are returned. If <group> does not reference a valid group ID, or <counter>
does not reference a valid counter within the group ID, an INVALID_VALUE
error is generated.
The command
void GenPerfMonitorsAMD(sizei n, uint *monitors)
returns a list of monitors. These monitors can then be used to select
groups/counters to be sampled, to start multiple monitoring sessions and to
return counter information sampled by the GPU. At creation time, the
performance monitor object has all counters disabled. The value of the
PERFMON_RESULT_AVAILABLE_AMD, PERFMON_RESULT_AMD, and
PERFMON_RESULT_SIZE_AMD queries will all initially be 0.
The command
void DeletePerfMonitorsAMD(sizei n, uint *monitors)
is used to delete the list of monitors created by a previous call to
GenPerfMonitors. If a monitor ID in the list <monitors> does not
reference a previously generated performance monitor, an INVALID_VALUE
error is generated.
The command
void SelectPerfMonitorCountersAMD(uint monitor, boolean enable,
uint group, int numCounters,
uint *counterList);
is used to enable or disable a list of counters from a group to be monitored
as identified by <monitor>. The <enable> argument determines whether the
counters should be enabled or disabled. <group> specifies the group
ID under which counters will be enabled or disabled. The <numCounters>
argument gives the number of counters to be selected from the list
<counterList>. If <monitor> is not a valid monitor created by
GenPerfMonitorsAMD, then INVALID_VALUE error will be generated. If <group>
is not a valid group, the INVALID_VALUE error will be generated. If
<numCounters> is less than 0, an INVALID_VALUE error will be generated.
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.
The command
void BeginPerfMonitorAMD(uint monitor);
is used to start a monitor session. Note that BeginPerfMonitor calls cannot
be nested. In addition, it is quite possible that given the list of groups
and counters/group enabled for a monitor, it may not be able to sample the
necessary counters and so the monitor session will fail. In such a case,
an INVALID_OPERATION error will be generated.
While BeginPerfMonitorAMD does mark the beginning of performance counter
collection, the counters do not begin collecting immediately. Rather, the
counters begin collection when BeginPerfMonitorAMD is processed by
the hardware. That is, the API is asynchronous, and performance counter
collection does not begin until the graphics hardware processes the
BeginPerfMonitorAMD command.
The command
void EndPerfMonitorAMD(uint monitor);
ends a monitor session started by BeginPerfMonitorAMD. If a performance
monitor is not currently started, an INVALID_OPERATION error will be
generated.
Note that there is an implied overhead to collecting performance counters
that may or may not distort performance depending on the implementation.
For example, some counters may require a pipeline flush thereby causing a
change in the performance of the application. Further, the frequency at
which an application samples may distort the accuracy of counters which are
variant (e.g., non-deterministic based on the input). While the effects
of sampling frequency are implementation dependent, general guidance can
be given that sampling at a high frequency may distort both performance
of the application and the accuracy of variant counters.
The command
void GetPerfMonitorCounterDataAMD(uint monitor, enum pname,
sizei dataSize,
uint *data, sizei *bytesWritten);
is used to return counter values that have been sampled for a monitor
session. If <pname> is PERFMON_RESULT_AVAILABLE_AMD, then <data> will
indicate whether the result is available or not. If <pname> is
PERFMON_RESULT_SIZE_AMD, <data> will contain actual size of all counter
results being sampled. If <pname> is PERFMON_RESULT_AMD, <data> will
contain results. For each counter of a group that was selected to be
sampled, the information is returned as group ID, followed by counter ID,
followed by counter value. The size of counter value returned will depend
on the counter value type. The argument <dataSize> specifies the number of
bytes available in the <data> buffer for writing. If <bytesWritten> is not
NULL, it gives the number of bytes written into the <data> buffer. It is an
INVALID_OPERATION error for <data> to be NULL. If <pname> is
PERFMON_RESULT_AMD and <dataSize> is less than the number of bytes required
to store the results as reported by a PERFMON_RESULT_SIZE_AMD query, then
results will be written only up to the number of bytes specified by
<dataSize>.
If no BeginPerfMonitorAMD/EndPerfMonitorAMD has been issued for a monitor,
then the result of querying for PERFMON_RESULT_AVAILABLE and
PERFMON_RESULT_SIZE will be 0. When SelectPerfMonitorCountersAMD is called
on a monitor, the results stored for the monitor become invalidated and
the value of PERFMON_RESULT_AVAILABLE and PERFMON_RESULT_SIZE queries should
behave as if no BeginPerfMonitorAMD/EndPerfMonitorAMD has been issued for
the monitor.
Errors
INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is unable
to begin monitoring with the currently selected counters.
INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is called
when a performance monitor is already active.
INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
when a performance monitor is not currently started.
INVALID_VALUE error will be generated if the <group> parameter to
GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
SelectPerfMonitorCountersAMD does not reference a valid group ID.
INVALID_VALUE error will be generated if the <counter> parameter to
GetPerfMonitorCounterInfoAMD does not reference a valid counter ID
in the group specified by <group>.
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.
INVALID_VALUE error will be generated if the <monitor> parameter to
SelectPerfMonitorCountersAMD does not reference a monitor created by
GenPerfMonitorsAMD.
INVALID_VALUE error will be generated if the <numCounters> parameter to
SelectPerfMonitorCountersAMD is less than 0.
New State
Sample Usage
typedef struct
{
GLuint *counterList;
int numCounters;
int maxActiveCounters;
} CounterInfo;
void
getGroupAndCounterList(GLuint **groupsList, int *numGroups,
CounterInfo **counterInfo)
{
GLint n;
GLuint *groups;
CounterInfo *counters;
glGetPerfMonitorGroupsAMD(&n, 0, NULL);
groups = (GLuint*) malloc(n * sizeof(GLuint));
glGetPerfMonitorGroupsAMD(NULL, n, groups);
*numGroups = n;
*groupsList = groups;
counters = (CounterInfo*) malloc(sizeof(CounterInfo) * n);
for (int i = 0 ; i < n; i++ )
{
glGetPerfMonitorCountersAMD(groups[i], &counters[i].numCounters,
&counters[i].maxActiveCounters, 0, NULL);
counters[i].counterList = (GLuint*)malloc(counters[i].numCounters *
sizeof(int));
glGetPerfMonitorCountersAMD(groups[i], NULL, NULL,
counters[i].numCounters,
counters[i].counterList);
}
*counterInfo = counters;
}
static int countersInitialized = 0;
int
getCounterByName(char *groupName, char *counterName, GLuint *groupID,
GLuint *counterID)
{
int numGroups;
GLuint *groups;
CounterInfo *counters;
int i = 0;
if (!countersInitialized)
{
getGroupAndCounterList(&groups, &numGroups, &counters);
countersInitialized = 1;
}
for ( i = 0; i < numGroups; i++ )
{
char curGroupName[256];
glGetPerfMonitorGroupStringAMD(groups[i], 256, NULL, curGroupName);
if (strcmp(groupName, curGroupName) == 0)
{
*groupID = groups[i];
break;
}
}
if ( i == numGroups )
return -1; // error - could not find the group name
for ( int j = 0; j < counters[i].numCounters; j++ )
{
char curCounterName[256];
glGetPerfMonitorCounterStringAMD(groups[i],
counters[i].counterList[j],
256, NULL, curCounterName);
if (strcmp(counterName, curCounterName) == 0)
{
*counterID = counters[i].counterList[j];
return 0;
}
}
return -1; // error - could not find the counter name
}
void
drawFrameWithCounters(void)
{
GLuint group[2];
GLuint counter[2];
GLuint monitor;
GLuint *counterData;
// Get group/counter IDs by name. Note that normally the
// counter and group names need to be queried for because
// each implementation of this extension on different hardware
// could define different names and groups. This is just provided
// to demonstrate the API.
getCounterByName("HW", "Hardware Busy", &group[0],
&counter[0]);
getCounterByName("API", "Draw Calls", &group[1],
&counter[1]);
// create perf monitor ID
glGenPerfMonitorsAMD(1, &monitor);
// enable the counters
glSelectPerfMonitorCountersAMD(monitor, GL_TRUE, group[0], 1,
&counter[0]);
glSelectPerfMonitorCountersAMD(monitor, GL_TRUE, group[1], 1,
&counter[1]);
glBeginPerfMonitorAMD(monitor);
// RENDER FRAME HERE
// ...
glEndPerfMonitorAMD(monitor);
// read the counters
GLint resultSize;
glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_SIZE_AMD,
sizeof(GLint), &resultSize, NULL);
counterData = (GLuint*) malloc(resultSize);
GLsizei bytesWritten;
glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AMD,
resultSize, counterData, &bytesWritten);
// display or log counter info
GLsizei wordCount = 0;
while ( (4 * wordCount) < bytesWritten )
{
GLuint groupId = counterData[wordCount];
GLuint counterId = counterData[wordCount + 1];
// Determine the counter type
GLuint counterType;
glGetPerfMonitorCounterInfoAMD(groupId, counterId,
GL_COUNTER_TYPE_AMD, &counterType);
if ( counterType == GL_UNSIGNED_INT64_AMD )
{
unsigned __int64 counterResult =
*(unsigned __int64*)(&counterData[wordCount + 2]);
// Print counter result
wordCount += 4;
}
else if ( counterType == GL_FLOAT )
{
float counterResult = *(float*)(&counterData[wordCount + 2]);
// Print counter result
wordCount += 3;
}
// else if ( ... ) check for other counter types
// (GL_UNSIGNED_INT and GL_PERCENTAGE_AMD)
}
}
Revision History
11/29/2007 - dginsburg
+ Clarified the default state of a performance monitor object on creation
11/09/2007 - dginsbur
+ Clarify what happens if SelectPerfMonitorCountersAMD is called on
a monitor with outstanding query results.
+ Rename counterSize to countersSize
+ Remove some ';' typos
06/13/2007 - dginsbur
+ Add language on the asynchronous nature of the API and
counter accuracy/performance distortion.
+ Add myself as the contact
+ Remove INVALID_OPERATION error when countersList is NULL
+ Clarify 64-bit issue
+ Make PERCENTAGE_AMD counters float rather than uint
+ Clarify accuracy distortion on variant counters only
+ Tweak to overview language
06/09/2007 - dginsbur
+ Fill in errors section and make many more errors explicit
+ Fix the example code so it compiles
06/08/2007 - dginsbur
+ Modified GetPerfMonitorGroupString and GetPerfMonitorCounterString to
be more client/server friendly.
+ Modified example.
+ Renamed parameters/variables to follow GL conventions.
+ Modified several 'int' param types to 'sizei'
+ Modifid counters type from 'int' to 'uint'
+ Renamed argument 'cb' and 'cbret'
+ Better documented GetPerfMonitorCounterData
+ Add AMD adornment in many places that were missing it
06/07/2007 - dginsbur
+ Cleanup formatting, remove tabs, make fit in proper page width
+ Add FLOAT and UNSIGNED_INT to list of COUNTER_TYPEs
+ Fix some bugs in the example code
+ Rewrite introduction
+ Clarified Issue 1 reasoning
+ Added Issue 3 regarding use of 64-bit data types
+ Added revision history
03/21/2007 - Initial version written. Written by amunshi.