/*
* 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 "util/bitset.h"
#include "util/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 inline void
init_groups(struct gl_context *ctx)
{
if (unlikely(!ctx->PerfMonitor.Groups))
ctx->Driver.InitPerfMonitorGroups(ctx);
}
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->Name = index;
m->Active = false;
m->ActiveGroups =
rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
m->ActiveCounters =
ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
goto fail;
for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
BITSET_WORDS(g->NumCounters));
if (m->ActiveCounters[i] == NULL)
goto fail;
}
return m;
fail:
ralloc_free(m->ActiveGroups);
ralloc_free(m->ActiveCounters);
ctx->Driver.DeletePerfMonitor(ctx, m);
return NULL;
}
static void
free_performance_monitor(GLuint key, void *data, void *user)
{
struct gl_perf_monitor_object *m = data;
struct gl_context *ctx = user;
ralloc_free(m->ActiveGroups);
ralloc_free(m->ActiveCounters);
ctx->Driver.DeletePerfMonitor(ctx, m);
}
void
_mesa_free_performance_monitors(struct gl_context *ctx)
{
_mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
free_performance_monitor, ctx);
_mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
}
static inline struct gl_perf_monitor_object *
lookup_monitor(struct gl_context *ctx, GLuint id)
{
return (struct gl_perf_monitor_object *)
_mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
}
static inline const struct gl_perf_monitor_group *
get_group(const struct gl_context *ctx, GLuint id)
{
if (id >= ctx->PerfMonitor.NumGroups)
return NULL;
return &ctx->PerfMonitor.Groups[id];
}
static inline const struct gl_perf_monitor_counter *
get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
{
if (id >= group_obj->NumCounters)
return NULL;
return &group_obj->Counters[id];
}
/* For INTEL_performance_query, query id 0 is reserved to be invalid. We use
* index to Groups array + 1 as the query id. Same applies to counter id.
*/
static inline GLuint
queryid_to_index(GLuint queryid)
{
return queryid - 1;
}
static inline GLuint
index_to_queryid(GLuint index)
{
return index + 1;
}
static inline bool
queryid_valid(const struct gl_context *ctx, GLuint queryid)
{
return get_group(ctx, queryid_to_index(queryid)) != NULL;
}
static inline GLuint
counterid_to_index(GLuint counterid)
{
return counterid - 1;
}
/*****************************************************************************/
void GLAPIENTRY
_mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
GLuint *groups)
{
GET_CURRENT_CONTEXT(ctx);
init_groups(ctx);
if (numGroups != NULL)
*numGroups = ctx->PerfMonitor.NumGroups;
if (groupsSize > 0 && groups != NULL) {
unsigned i;
unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
/* We just use the index in the Groups array as the ID. */
for (i = 0; i < n; i++)
groups[i] = i;
}
}
void GLAPIENTRY
_mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
GLint *maxActiveCounters,
GLsizei countersSize, GLuint *counters)
{
GET_CURRENT_CONTEXT(ctx);
const struct gl_perf_monitor_group *group_obj;
init_groups(ctx);
group_obj = get_group(ctx, group);
if (group_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfMonitorCountersAMD(invalid group)");
return;
}
if (maxActiveCounters != NULL)
*maxActiveCounters = group_obj->MaxActiveCounters;
if (numCounters != NULL)
*numCounters = group_obj->NumCounters;
if (counters != NULL) {
unsigned i;
unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
for (i = 0; i < n; i++) {
/* We just use the index in the Counters array as the ID. */
counters[i] = i;
}
}
}
void GLAPIENTRY
_mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
GLsizei *length, GLchar *groupString)
{
GET_CURRENT_CONTEXT(ctx);
const struct gl_perf_monitor_group *group_obj;
init_groups(ctx);
group_obj = get_group(ctx, group);
if (group_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
return;
}
if (bufSize == 0) {
/* Return the number of characters that would be required to hold the
* group string, excluding the null terminator.
*/
if (length != NULL)
*length = strlen(group_obj->Name);
} else {
if (length != NULL)
*length = MIN2(strlen(group_obj->Name), bufSize);
if (groupString != NULL)
strncpy(groupString, group_obj->Name, bufSize);
}
}
void GLAPIENTRY
_mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
GLsizei bufSize, GLsizei *length,
GLchar *counterString)
{
GET_CURRENT_CONTEXT(ctx);
const struct gl_perf_monitor_group *group_obj;
const struct gl_perf_monitor_counter *counter_obj;
init_groups(ctx);
group_obj = get_group(ctx, group);
if (group_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfMonitorCounterStringAMD(invalid group)");
return;
}
counter_obj = get_counter(group_obj, counter);
if (counter_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfMonitorCounterStringAMD(invalid counter)");
return;
}
if (bufSize == 0) {
/* Return the number of characters that would be required to hold the
* counter string, excluding the null terminator.
*/
if (length != NULL)
*length = strlen(counter_obj->Name);
} else {
if (length != NULL)
*length = MIN2(strlen(counter_obj->Name), bufSize);
if (counterString != NULL)
strncpy(counterString, counter_obj->Name, bufSize);
}
}
void GLAPIENTRY
_mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
GLvoid *data)
{
GET_CURRENT_CONTEXT(ctx);
const struct gl_perf_monitor_group *group_obj;
const struct gl_perf_monitor_counter *counter_obj;
init_groups(ctx);
group_obj = get_group(ctx, group);
if (group_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfMonitorCounterInfoAMD(invalid group)");
return;
}
counter_obj = get_counter(group_obj, counter);
if (counter_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfMonitorCounterInfoAMD(invalid counter)");
return;
}
switch (pname) {
case GL_COUNTER_TYPE_AMD:
*((GLenum *) data) = counter_obj->Type;
break;
case GL_COUNTER_RANGE_AMD:
switch (counter_obj->Type) {
case GL_FLOAT:
case GL_PERCENTAGE_AMD: {
float *f_data = data;
f_data[0] = counter_obj->Minimum.f;
f_data[1] = counter_obj->Maximum.f;
break;
}
case GL_UNSIGNED_INT: {
uint32_t *u32_data = data;
u32_data[0] = counter_obj->Minimum.u32;
u32_data[1] = counter_obj->Maximum.u32;
break;
}
case GL_UNSIGNED_INT64_AMD: {
uint64_t *u64_data = data;
u64_data[0] = counter_obj->Minimum.u64;
u64_data[1] = counter_obj->Maximum.u64;
break;
}
default:
assert(!"Should not get here: invalid counter type");
}
break;
default:
_mesa_error(ctx, GL_INVALID_ENUM,
"glGetPerfMonitorCounterInfoAMD(pname)");
return;
}
}
void GLAPIENTRY
_mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
{
GLuint first;
GET_CURRENT_CONTEXT(ctx);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
init_groups(ctx);
if (n < 0) {
_mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
return;
}
if (monitors == NULL)
return;
/* 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);
m->Ended = false;
}
_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);
int i;
struct gl_perf_monitor_object *m;
const struct gl_perf_monitor_group *group_obj;
m = lookup_monitor(ctx, monitor);
/* "INVALID_VALUE error will be generated if the <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;
m->Ended = false;
} else {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glBeginPerfMonitor(driver unable to begin monitoring)");
}
}
void GLAPIENTRY
_mesa_EndPerfMonitorAMD(GLuint monitor)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
if (m == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
return;
}
/* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
* when a performance monitor is not currently started."
*/
if (!m->Active) {
_mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)");
return;
}
ctx->Driver.EndPerfMonitor(ctx, m);
m->Active = false;
m->Ended = true;
}
/**
* Return the number of bytes needed to store a monitor's result.
*/
static unsigned
perf_monitor_result_size(const struct gl_context *ctx,
const struct gl_perf_monitor_object *m)
{
unsigned group, counter;
unsigned size = 0;
for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
BITSET_WORD tmp;
BITSET_FOREACH_SET(counter, tmp, m->ActiveCounters[group], g->NumCounters) {
const struct gl_perf_monitor_counter *c = &g->Counters[counter];
size += sizeof(uint32_t); /* Group ID */
size += sizeof(uint32_t); /* Counter ID */
size += _mesa_perf_monitor_counter_size(c);
}
}
return size;
}
void GLAPIENTRY
_mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
GLsizei dataSize, GLuint *data,
GLint *bytesWritten)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
bool result_available;
if (m == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfMonitorCounterDataAMD(invalid monitor)");
return;
}
/* "It is an INVALID_OPERATION error for <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;
}
/* If the monitor has never ended, there is no result. */
result_available = m->Ended &&
ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
/* AMD appears to return 0 for all queries unless a result is available. */
if (!result_available) {
*data = 0;
if (bytesWritten != NULL)
*bytesWritten = sizeof(GLuint);
return;
}
switch (pname) {
case GL_PERFMON_RESULT_AVAILABLE_AMD:
*data = 1;
if (bytesWritten != NULL)
*bytesWritten = sizeof(GLuint);
break;
case GL_PERFMON_RESULT_SIZE_AMD:
*data = perf_monitor_result_size(ctx, m);
if (bytesWritten != NULL)
*bytesWritten = sizeof(GLuint);
break;
case GL_PERFMON_RESULT_AMD:
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;
}
}
extern void GLAPIENTRY
_mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
{
GET_CURRENT_CONTEXT(ctx);
unsigned numGroups;
init_groups(ctx);
/* The GL_INTEL_performance_query spec says:
*
* "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
*/
if (!queryId) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetFirstPerfQueryIdINTEL(queryId == NULL)");
return;
}
numGroups = ctx->PerfMonitor.NumGroups;
/* The GL_INTEL_performance_query spec says:
*
* "If the given hardware platform doesn't support any performance
* queries, then the value of 0 is returned and INVALID_OPERATION error
* is raised."
*/
if (numGroups == 0) {
*queryId = 0;
_mesa_error(ctx, GL_INVALID_OPERATION,
"glGetFirstPerfQueryIdINTEL(no queries supported)");
return;
}
*queryId = index_to_queryid(0);
}
extern void GLAPIENTRY
_mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId)
{
GET_CURRENT_CONTEXT(ctx);
init_groups(ctx);
/* The GL_INTEL_performance_query spec says:
*
* "The result is passed in location pointed by nextQueryId. If query
* identified by queryId is the last query available the value of 0 is
* returned. If the specified performance query identifier is invalid
* then INVALID_VALUE error is generated. If nextQueryId pointer is
* equal to 0, an INVALID_VALUE error is generated. Whenever error is
* generated, the value of 0 is returned."
*/
if (!nextQueryId) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
return;
}
if (!queryid_valid(ctx, queryId)) {
*nextQueryId = 0;
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetNextPerfQueryIdINTEL(invalid query)");
return;
}
++queryId;
if (!queryid_valid(ctx, queryId)) {
*nextQueryId = 0;
} else {
*nextQueryId = queryId;
}
}
extern void GLAPIENTRY
_mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId)
{
GET_CURRENT_CONTEXT(ctx);
unsigned i;
init_groups(ctx);
/* The GL_INTEL_performance_query spec says:
*
* "If queryName does not reference a valid query name, an INVALID_VALUE
* error is generated."
*/
if (!queryName) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfQueryIdByNameINTEL(queryName == NULL)");
return;
}
/* The specification does not state that this produces an error. */
if (!queryId) {
_mesa_warning(ctx, "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
return;
}
for (i = 0; i < ctx->PerfMonitor.NumGroups; ++i) {
const struct gl_perf_monitor_group *group_obj = get_group(ctx, i);
if (strcmp(group_obj->Name, queryName) == 0) {
*queryId = index_to_queryid(i);
return;
}
}
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfQueryIdByNameINTEL(invalid query name)");
}
extern void GLAPIENTRY
_mesa_GetPerfQueryInfoINTEL(GLuint queryId,
GLuint queryNameLength, char *queryName,
GLuint *dataSize, GLuint *noCounters,
GLuint *noActiveInstances,
GLuint *capsMask)
{
GET_CURRENT_CONTEXT(ctx);
unsigned i;
const struct gl_perf_monitor_group *group_obj;
init_groups(ctx);
group_obj = get_group(ctx, queryid_to_index(queryId));
if (group_obj == NULL) {
/* The GL_INTEL_performance_query spec says:
*
* "If queryId does not reference a valid query type, an
* INVALID_VALUE error is generated."
*/
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfQueryInfoINTEL(invalid query)");
return;
}
if (queryName) {
strncpy(queryName, group_obj->Name, queryNameLength);
/* No specification given about whether the string needs to be
* zero-terminated. Zero-terminate the string always as we don't
* otherwise communicate the length of the returned string.
*/
if (queryNameLength > 0) {
queryName[queryNameLength - 1] = '\0';
}
}
if (dataSize) {
unsigned size = 0;
for (i = 0; i < group_obj->NumCounters; ++i) {
/* What we get from the driver is group id (uint32_t) + counter id
* (uint32_t) + value.
*/
size += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]);
}
*dataSize = size;
}
if (noCounters) {
*noCounters = group_obj->NumCounters;
}
/* The GL_INTEL_performance_query spec says:
*
* "-- the actual number of already created query instances in
* maxInstances location"
*
* 1) Typo in the specification, should be noActiveInstances.
* 2) Another typo in the specification, maxInstances parameter is not listed
* in the declaration of this function in the list of new functions.
*/
if (noActiveInstances) {
*noActiveInstances = _mesa_HashNumEntries(ctx->PerfMonitor.Monitors);
}
if (capsMask) {
/* TODO: This information not yet available in the monitor structs. For
* now, we hardcode SINGLE_CONTEXT, since that's what the implementation
* currently tries very hard to do.
*/
*capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL;
}
}
extern void GLAPIENTRY
_mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId,
GLuint counterNameLength, char *counterName,
GLuint counterDescLength, char *counterDesc,
GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum,
GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue)
{
GET_CURRENT_CONTEXT(ctx);
const struct gl_perf_monitor_group *group_obj;
const struct gl_perf_monitor_counter *counter_obj;
unsigned counterIndex;
unsigned i;
init_groups(ctx);
group_obj = get_group(ctx, queryid_to_index(queryId));
/* The GL_INTEL_performance_query spec says:
*
* "If the pair of queryId and counterId does not reference a valid
* counter, an INVALID_VALUE error is generated."
*/
if (group_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfCounterInfoINTEL(invalid queryId)");
return;
}
counterIndex = counterid_to_index(counterId);
counter_obj = get_counter(group_obj, counterIndex);
if (counter_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfCounterInfoINTEL(invalid counterId)");
return;
}
if (counterName) {
strncpy(counterName, counter_obj->Name, counterNameLength);
/* No specification given about whether the string needs to be
* zero-terminated. Zero-terminate the string always as we don't
* otherwise communicate the length of the returned string.
*/
if (counterNameLength > 0) {
counterName[counterNameLength - 1] = '\0';
}
}
if (counterDesc) {
/* TODO: No separate description text at the moment. We pass the name
* again for the moment.
*/
strncpy(counterDesc, counter_obj->Name, counterDescLength);
/* No specification given about whether the string needs to be
* zero-terminated. Zero-terminate the string always as we don't
* otherwise communicate the length of the returned string.
*/
if (counterDescLength > 0) {
counterDesc[counterDescLength - 1] = '\0';
}
}
if (counterOffset) {
unsigned offset = 0;
for (i = 0; i < counterIndex; ++i) {
/* What we get from the driver is group id (uint32_t) + counter id
* (uint32_t) + value.
*/
offset += 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj->Counters[i]);
}
*counterOffset = 2 * sizeof(uint32_t) + offset;
}
if (counterDataSize) {
*counterDataSize = _mesa_perf_monitor_counter_size(counter_obj);
}
if (counterTypeEnum) {
/* TODO: Different counter types (semantic type, not data type) not
* supported as of yet.
*/
*counterTypeEnum = GL_PERFQUERY_COUNTER_RAW_INTEL;
}
if (counterDataTypeEnum) {
switch (counter_obj->Type) {
case GL_FLOAT:
case GL_PERCENTAGE_AMD:
*counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL;
break;
case GL_UNSIGNED_INT:
*counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL;
break;
case GL_UNSIGNED_INT64_AMD:
*counterDataTypeEnum = GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL;
break;
default:
assert(!"Should not get here: invalid counter type");
return;
}
}
if (rawCounterMaxValue) {
/* This value is (implicitly) specified to be used only with
* GL_PERFQUERY_COUNTER_RAW_INTEL counters. When semantic types for
* counters are added, that needs to be checked.
*/
/* The GL_INTEL_performance_query spec says:
*
* "for some raw counters for which the maximal value is
* deterministic, the maximal value of the counter in 1 second is
* returned in the location pointed by rawCounterMaxValue, otherwise,
* the location is written with the value of 0."
*
* The maximum value reported by the driver at the moment is not with
* these semantics, so write 0 always to be safe.
*/
*rawCounterMaxValue = 0;
}
}
extern void GLAPIENTRY
_mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle)
{
GET_CURRENT_CONTEXT(ctx);
GLuint first;
GLuint group;
const struct gl_perf_monitor_group *group_obj;
struct gl_perf_monitor_object *m;
unsigned i;
init_groups(ctx);
/* This is not specified in the extension, but is the only sane thing to
* do.
*/
if (queryHandle == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glCreatePerfQueryINTEL(queryHandle == NULL)");
return;
}
group = queryid_to_index(queryId);
group_obj = get_group(ctx, group);
/* The GL_INTEL_performance_query spec says:
*
* "If queryId does not reference a valid query type, an INVALID_VALUE
* error is generated."
*/
if (group_obj == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glCreatePerfQueryINTEL(invalid queryId)");
return;
}
/* The query object created here is the counterpart of a `monitor' in
* AMD_performance_monitor. This call is equivalent to calling
* GenPerfMonitorsAMD and SelectPerfMonitorCountersAMD with a list of all
* counters in a group.
*/
/* We keep the monitor ids contiguous */
first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, 1);
if (!first) {
/* The GL_INTEL_performance_query spec says:
*
* "If the query instance cannot be created due to exceeding the
* number of allowed instances or driver fails query creation due to
* an insufficient memory reason, an OUT_OF_MEMORY error is
* generated, and the location pointed by queryHandle returns NULL."
*/
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glCreatePerfQueryINTEL");
return;
}
m = new_performance_monitor(ctx, first);
if (m == NULL) {
_mesa_error_no_memory(__func__);
return;
}
_mesa_HashInsert(ctx->PerfMonitor.Monitors, first, m);
*queryHandle = first;
ctx->Driver.ResetPerfMonitor(ctx, m);
for (i = 0; i < group_obj->NumCounters; ++i) {
++m->ActiveGroups[group];
/* Counters are a continuous range of integers, 0 to NumCounters (excl),
* so i is the counter value to use here.
*/
BITSET_SET(m->ActiveCounters[group], i);
}
}
extern void GLAPIENTRY
_mesa_DeletePerfQueryINTEL(GLuint queryHandle)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_perf_monitor_object *m;
/* The queryHandle is the counterpart to AMD_performance_monitor's monitor
* id.
*/
m = lookup_monitor(ctx, queryHandle);
/* The GL_INTEL_performance_query spec says:
*
* "If a query handle doesn't reference a previously created performance
* query instance, an INVALID_VALUE error is generated."
*/
if (m == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glDeletePerfQueryINTEL(invalid queryHandle)");
return;
}
/* Let the driver stop the monitor if it's active. */
if (m->Active) {
ctx->Driver.ResetPerfMonitor(ctx, m);
m->Ended = false;
}
_mesa_HashRemove(ctx->PerfMonitor.Monitors, queryHandle);
ralloc_free(m->ActiveGroups);
ralloc_free(m->ActiveCounters);
ctx->Driver.DeletePerfMonitor(ctx, m);
}
extern void GLAPIENTRY
_mesa_BeginPerfQueryINTEL(GLuint queryHandle)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_perf_monitor_object *m;
/* The queryHandle is the counterpart to AMD_performance_monitor's monitor
* id.
*/
m = lookup_monitor(ctx, queryHandle);
/* The GL_INTEL_performance_query spec says:
*
* "If a query handle doesn't reference a previously created performance
* query instance, an INVALID_VALUE error is generated."
*/
if (m == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glBeginPerfQueryINTEL(invalid queryHandle)");
return;
}
/* The GL_INTEL_performance_query spec says:
*
* "Note that some query types, they cannot be collected in the same
* time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
* they refer to queries of such different types. In such case
* INVALID_OPERATION error is generated."
*
* We also generate an INVALID_OPERATION error if the driver can't begin
* monitoring for its own reasons, and for nesting the same query.
*/
if (m->Active) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glBeginPerfQueryINTEL(already active)");
return;
}
if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
m->Active = true;
m->Ended = false;
} else {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glBeginPerfQueryINTEL(driver unable to begin monitoring)");
}
}
extern void GLAPIENTRY
_mesa_EndPerfQueryINTEL(GLuint queryHandle)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_perf_monitor_object *m;
/* The queryHandle is the counterpart to AMD_performance_monitor's monitor
* id.
*/
m = lookup_monitor(ctx, queryHandle);
/* The GL_INTEL_performance_query spec says:
*
* "If a performance query is not currently started, an
* INVALID_OPERATION error will be generated."
*
* The specification doesn't state that an invalid handle would be an
* INVALID_VALUE error. Regardless, query for such a handle will not be
* started, so we generate an INVALID_OPERATION in that case too.
*/
if (m == NULL) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glEndPerfQueryINTEL(invalid queryHandle)");
return;
}
if (!m->Active) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glEndPerfQueryINTEL(not active)");
return;
}
ctx->Driver.EndPerfMonitor(ctx, m);
m->Active = false;
m->Ended = true;
}
extern void GLAPIENTRY
_mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
GLsizei dataSize, void *data, GLuint *bytesWritten)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_perf_monitor_object *m;
bool result_available;
/* The GL_INTEL_performance_query spec says:
*
* "If bytesWritten or data pointers are NULL then an INVALID_VALUE
* error is generated."
*/
if (!bytesWritten || !data) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
return;
}
/* The queryHandle is the counterpart to AMD_performance_monitor's monitor
* id.
*/
m = lookup_monitor(ctx, queryHandle);
/* The specification doesn't state that an invalid handle generates an
* error. We could interpret that to mean the case should be handled as
* "measurement not ready for this query", but what should be done if
* `flags' equals PERFQUERY_WAIT_INTEL?
*
* To resolve this, we just generate an INVALID_VALUE from an invalid query
* handle.
*/
if (m == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glGetPerfQueryDataINTEL(invalid queryHandle)");
return;
}
/* We need at least enough room for a single value. */
if (dataSize < sizeof(GLuint)) {
*bytesWritten = 0;
return;
}
/* The GL_INTEL_performance_query spec says:
*
* "The call may end without returning any data if they are not ready
* for reading as the measurement session is still pending (the
* EndPerfQueryINTEL() command processing is not finished by
* hardware). In this case location pointed by the bytesWritten
* parameter will be set to 0."
*
* If EndPerfQueryINTEL() is not called at all, we follow this.
*/
if (!m->Ended) {
*bytesWritten = 0;
return;
}
result_available = ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
if (!result_available) {
if (flags == GL_PERFQUERY_FLUSH_INTEL) {
ctx->Driver.Flush(ctx);
} else if (flags == GL_PERFQUERY_WAIT_INTEL) {
/* Assume Finish() is both enough and not too much to wait for
* results. If results are still not available after Finish(), the
* later code automatically bails out with 0 for bytesWritten.
*/
ctx->Driver.Finish(ctx);
result_available =
ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
}
}
if (result_available) {
ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, (GLint*)bytesWritten);
} else {
*bytesWritten = 0;
}
}