summaryrefslogtreecommitdiffstats
path: root/src/mesa
diff options
context:
space:
mode:
authorRobert Bragg <[email protected]>2015-04-29 08:17:20 +0100
committerRobert Bragg <[email protected]>2017-02-22 12:12:27 +0000
commitd83a33a9de94212b622053ab341655a3e92cc033 (patch)
treee32059fc25dc1cfa545bdeb8436f32244d9d297e /src/mesa
parentccc8720cf7d3cb538bde1cd158755bde2e36eefd (diff)
mesa: Separate INTEL_performance_query frontend
To allow the backend interfaces for AMD_performance_monitor and INTEL_performance_query to evolve independently based on the more specific requirements of each extension this starts by separating the frontends of these extensions. Even though there wasn't much tying these frontends together, this separation intentionally copies what few helpers/utilities that were shared between the two extensions, avoiding any re-factoring specific to INTEL_performance_query so that the evolution will be easier to follow later. Signed-off-by: Robert Bragg <[email protected]> Reviewed-by: Lionel Landwerlin <[email protected]> Reviewed-by: Kenneth Graunke <[email protected]>
Diffstat (limited to 'src/mesa')
-rw-r--r--src/mesa/Makefile.sources2
-rw-r--r--src/mesa/main/context.c3
-rw-r--r--src/mesa/main/mtypes.h15
-rw-r--r--src/mesa/main/performance_monitor.c590
-rw-r--r--src/mesa/main/performance_monitor.h39
-rw-r--r--src/mesa/main/performance_query.c784
-rw-r--r--src/mesa/main/performance_query.h84
7 files changed, 888 insertions, 629 deletions
diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources
index ee737b0d7f9..ae701138f5d 100644
--- a/src/mesa/Makefile.sources
+++ b/src/mesa/Makefile.sources
@@ -150,6 +150,8 @@ MAIN_FILES = \
main/pbo.h \
main/performance_monitor.c \
main/performance_monitor.h \
+ main/performance_query.c \
+ main/performance_query.h \
main/pipelineobj.c \
main/pipelineobj.h \
main/pixel.c \
diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
index bbc13a9a423..8349b88a27b 100644
--- a/src/mesa/main/context.c
+++ b/src/mesa/main/context.c
@@ -108,6 +108,7 @@
#include "matrix.h"
#include "multisample.h"
#include "performance_monitor.h"
+#include "performance_query.h"
#include "pipelineobj.h"
#include "pixel.h"
#include "pixelstore.h"
@@ -836,6 +837,7 @@ init_attrib_groups(struct gl_context *ctx)
_mesa_init_matrix( ctx );
_mesa_init_multisample( ctx );
_mesa_init_performance_monitors( ctx );
+ _mesa_init_performance_queries( ctx );
_mesa_init_pipeline( ctx );
_mesa_init_pixel( ctx );
_mesa_init_pixelstore( ctx );
@@ -1325,6 +1327,7 @@ _mesa_free_context_data( struct gl_context *ctx )
_mesa_free_varray_data(ctx);
_mesa_free_transform_feedback(ctx);
_mesa_free_performance_monitors(ctx);
+ _mesa_free_performance_queries(ctx);
_mesa_reference_buffer_object(ctx, &ctx->Pack.BufferObj, NULL);
_mesa_reference_buffer_object(ctx, &ctx->Unpack.BufferObj, NULL);
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index 377cdce4ff4..9f4f9613943 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -1874,6 +1874,20 @@ struct gl_perf_monitor_state
/**
+ * Context state for INTEL_performance_query.
+ */
+struct gl_perf_query_state
+{
+ /** Array of performance monitor groups (indexed by group ID) */
+ const struct gl_perf_monitor_group *Groups;
+ GLuint NumGroups;
+
+ /** The table of all performance query objects. */
+ struct _mesa_HashTable *Objects;
+};
+
+
+/**
* Names of the various vertex/fragment program register files, etc.
*
* NOTE: first four tokens must fit into 2 bits (see t_vb_arbprogram.c)
@@ -4526,6 +4540,7 @@ struct gl_context
struct gl_transform_feedback_state TransformFeedback;
struct gl_perf_monitor_state PerfMonitor;
+ struct gl_perf_query_state PerfQuery;
struct gl_buffer_object *DrawIndirectBuffer; /** < GL_ARB_draw_indirect */
struct gl_buffer_object *ParameterBuffer; /** < GL_ARB_indirect_parameters */
diff --git a/src/mesa/main/performance_monitor.c b/src/mesa/main/performance_monitor.c
index 43529b2b35a..a4a2f9e5970 100644
--- a/src/mesa/main/performance_monitor.c
+++ b/src/mesa/main/performance_monitor.c
@@ -685,593 +685,3 @@ _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
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;
- }
-}
diff --git a/src/mesa/main/performance_monitor.h b/src/mesa/main/performance_monitor.h
index 7b311e4e01d..88de14546dc 100644
--- a/src/mesa/main/performance_monitor.h
+++ b/src/mesa/main/performance_monitor.h
@@ -87,43 +87,4 @@ unsigned
_mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *);
-extern void GLAPIENTRY
-_mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId);
-
-extern void GLAPIENTRY
-_mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId);
-
-extern void GLAPIENTRY
-_mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId);
-
-extern void GLAPIENTRY
-_mesa_GetPerfQueryInfoINTEL(GLuint queryId,
- GLuint queryNameLength, char *queryName,
- GLuint *dataSize, GLuint *noCounters,
- GLuint *noActiveInstances,
- GLuint *capsMask);
-
-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);
-
-extern void GLAPIENTRY
-_mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle);
-
-extern void GLAPIENTRY
-_mesa_DeletePerfQueryINTEL(GLuint queryHandle);
-
-extern void GLAPIENTRY
-_mesa_BeginPerfQueryINTEL(GLuint queryHandle);
-
-extern void GLAPIENTRY
-_mesa_EndPerfQueryINTEL(GLuint queryHandle);
-
-extern void GLAPIENTRY
-_mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
- GLsizei dataSize, void *data, GLuint *bytesWritten);
-
#endif
diff --git a/src/mesa/main/performance_query.c b/src/mesa/main/performance_query.c
new file mode 100644
index 00000000000..f0ecabe3b58
--- /dev/null
+++ b/src/mesa/main/performance_query.c
@@ -0,0 +1,784 @@
+/*
+ * 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_query.c
+ * Core Mesa support for the INTEL_performance_query 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_query.h"
+#include "util/bitset.h"
+#include "util/ralloc.h"
+
+void
+_mesa_init_performance_queries(struct gl_context *ctx)
+{
+ ctx->PerfQuery.Objects = _mesa_NewHashTable();
+ ctx->PerfQuery.NumGroups = 0;
+ ctx->PerfQuery.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_query(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->PerfQuery.NumGroups);
+
+ m->ActiveCounters =
+ ralloc_array(NULL, BITSET_WORD *, ctx->PerfQuery.NumGroups);
+
+ if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
+ goto fail;
+
+ for (i = 0; i < ctx->PerfQuery.NumGroups; i++) {
+ const struct gl_perf_monitor_group *g = &ctx->PerfQuery.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_query(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_queries(struct gl_context *ctx)
+{
+ _mesa_HashDeleteAll(ctx->PerfQuery.Objects,
+ free_performance_query, ctx);
+ _mesa_DeleteHashTable(ctx->PerfQuery.Objects);
+}
+
+static inline struct gl_perf_monitor_object *
+lookup_query(struct gl_context *ctx, GLuint id)
+{
+ return (struct gl_perf_monitor_object *)
+ _mesa_HashLookup(ctx->PerfQuery.Objects, id);
+}
+
+static inline const struct gl_perf_monitor_group *
+get_group(const struct gl_context *ctx, GLuint id)
+{
+ if (id >= ctx->PerfQuery.NumGroups)
+ return NULL;
+
+ return &ctx->PerfQuery.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;
+}
+
+/*****************************************************************************/
+
+/**
+ * Returns how many bytes a counter's value takes up.
+ */
+unsigned
+_mesa_perf_query_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->PerfQuery.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->PerfQuery.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_query_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->PerfQuery.Objects);
+ }
+
+ 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_query_counter_size(&group_obj->Counters[i]);
+ }
+ *counterOffset = 2 * sizeof(uint32_t) + offset;
+ }
+
+ if (counterDataSize) {
+ *counterDataSize = _mesa_perf_query_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 query ids contiguous */
+ first = _mesa_HashFindFreeKeyBlock(ctx->PerfQuery.Objects, 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_query(ctx, first);
+ if (m == NULL) {
+ _mesa_error_no_memory(__func__);
+ return;
+ }
+
+ _mesa_HashInsert(ctx->PerfQuery.Objects, 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_query(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 query if it's active. */
+ if (m->Active) {
+ ctx->Driver.ResetPerfMonitor(ctx, m);
+ m->Ended = false;
+ }
+
+ _mesa_HashRemove(ctx->PerfQuery.Objects, 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_query(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
+ * a query 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 query)");
+ }
+}
+
+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_query(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_query(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;
+ }
+}
diff --git a/src/mesa/main/performance_query.h b/src/mesa/main/performance_query.h
new file mode 100644
index 00000000000..3fed5eae1b8
--- /dev/null
+++ b/src/mesa/main/performance_query.h
@@ -0,0 +1,84 @@
+/*
+ * 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_query.h
+ * Core Mesa support for the INTEL_performance_query extension
+ */
+
+#pragma once
+#ifndef PERFORMANCE_QUERY_H
+#define PERFORMANCE_QUERY_H
+
+#include "glheader.h"
+
+extern void
+_mesa_init_performance_queries(struct gl_context *ctx);
+
+extern void
+_mesa_free_performance_queries(struct gl_context *ctx);
+
+unsigned
+_mesa_perf_query_counter_size(const struct gl_perf_monitor_counter *);
+
+
+extern void GLAPIENTRY
+_mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId);
+
+extern void GLAPIENTRY
+_mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId);
+
+extern void GLAPIENTRY
+_mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId);
+
+extern void GLAPIENTRY
+_mesa_GetPerfQueryInfoINTEL(GLuint queryId,
+ GLuint queryNameLength, char *queryName,
+ GLuint *dataSize, GLuint *noCounters,
+ GLuint *noActiveInstances,
+ GLuint *capsMask);
+
+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);
+
+extern void GLAPIENTRY
+_mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle);
+
+extern void GLAPIENTRY
+_mesa_DeletePerfQueryINTEL(GLuint queryHandle);
+
+extern void GLAPIENTRY
+_mesa_BeginPerfQueryINTEL(GLuint queryHandle);
+
+extern void GLAPIENTRY
+_mesa_EndPerfQueryINTEL(GLuint queryHandle);
+
+extern void GLAPIENTRY
+_mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
+ GLsizei dataSize, void *data, GLuint *bytesWritten);
+
+#endif