[Mesa-dev] [PATCH 5/6] mesa: Implement INTEL_performance_query.
Ian Romanick
idr at freedesktop.org
Wed Mar 12 13:34:15 PDT 2014
On 03/12/2014 05:54 AM, Petri Latvala wrote:
> Using the existing driver hooks made for AMD_performance_monitor, implement
> INTEL_performance_query functions.
>
> Signed-off-by: Petri Latvala <petri.latvala at intel.com>
> ---
> src/mesa/main/performance_monitor.c | 476 +++++++++++++++++++++++++++++++++---
> 1 file changed, 439 insertions(+), 37 deletions(-)
>
> diff --git a/src/mesa/main/performance_monitor.c b/src/mesa/main/performance_monitor.c
> index 183a895..bf58d45 100644
> --- a/src/mesa/main/performance_monitor.c
> +++ b/src/mesa/main/performance_monitor.c
> @@ -137,6 +137,46 @@ get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
> 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;
> +}
> +
> +static inline GLuint
> +index_to_counterid(GLuint index)
> +{
> + return index + 1;
> +}
> +
> +static inline bool
> +counterid_valid(const struct gl_perf_monitor_group *group_obj,
> + GLuint counterid)
> +{
> + return get_counter(group_obj, counterid_to_index(counterid)) != NULL;
> +}
> +
> /*****************************************************************************/
>
> void GLAPIENTRY
> @@ -645,19 +685,29 @@ _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
> {
> GET_CURRENT_CONTEXT(ctx);
>
> + unsigned numGroups;
> +
> /* "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
> */
> if (!queryId) {
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
> + _mesa_error(ctx, GL_INVALID_VALUE,
> + "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
The whitespace-only changes should go in the previous patch. No reason
to add a line in one patch, then change the spacing in the next.
> return;
> }
>
> + numGroups = ctx->PerfMonitor.NumGroups;
> +
> /* "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 = 0;
> - _mesa_error(ctx, GL_INVALID_OPERATION, "glGetFirstPerfQueryIdINTEL(no queries supported)");
> + *queryId = index_to_queryid(0);
> }
>
> extern void GLAPIENTRY
> @@ -667,22 +717,34 @@ _mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId)
>
> /* "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) {
> - *nextQueryId = 0;
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
> + _mesa_error(ctx, GL_INVALID_VALUE,
> + "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
> return;
> }
>
> /* "If the specified performance
> * query identifier is invalid then INVALID_VALUE error is generated."
> - *
> - * No queries are supported, so all queries are invalid.
> */
> - *nextQueryId = 0;
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetNextPerfQueryIdINTEL(invalid query)");
> + if (!queryid_valid(ctx, queryId)) {
> + /* "Whenever error is generated, the value of 0 is returned." */
> + *nextQueryId = 0;
> + _mesa_error(ctx, GL_INVALID_VALUE,
> + "glGetNextPerfQueryIdINTEL(invalid query)");
> + return;
> + }
> +
> + ++queryId;
> +
> + /* "If query identified by queryId is the last query available the value of
> + * 0 is returned."
> + */
> + if (!queryid_valid(ctx, queryId)) {
> + *nextQueryId = 0;
> + } else {
> + *nextQueryId = queryId;
> + }
> }
>
> extern void GLAPIENTRY
> @@ -690,13 +752,36 @@ _mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId)
> {
> GET_CURRENT_CONTEXT(ctx);
>
> + unsigned i;
> +
> + /* "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) {
> + 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;
> + }
> + }
> +
> /* "If queryName does not reference a valid query name, an INVALID_VALUE
> * error is generated."
> - *
> - * No queries are supported, so all query names are invalid.
> */
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfQueryIdByNameINTEL(invalid query name)");
> + _mesa_error(ctx, GL_INVALID_VALUE,
> + "glGetPerfQueryIdByNameINTEL(invalid query name)");
> }
>
> extern void GLAPIENTRY
> @@ -707,14 +792,65 @@ _mesa_GetPerfQueryInfoINTEL(GLuint queryId,
> GLuint *capsMask)
> {
> GET_CURRENT_CONTEXT(ctx);
> + unsigned i;
>
> - /* "If queryId does not reference a valid query type, an INVALID_VALUE error is
> - * generated."
> + const struct gl_perf_monitor_group *group_obj;
> +
> + group_obj = get_group(ctx, queryid_to_index(queryId));
> +
> + if (group_obj == NULL) {
> + /* "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 anyway, no way for the
> + * application to know if the buffer was large enough.
> + */
> + 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 actual number of already created query instances in maxInstances
> + * location"
> *
> - * No queries are supported, so all queries are invalid.
> + * 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);
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfQueryInfoINTEL(invalid query)");
> + 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
> @@ -726,73 +862,288 @@ _mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId,
> {
> GET_CURRENT_CONTEXT(ctx);
>
> + const struct gl_perf_monitor_group *group_obj;
> + const struct gl_perf_monitor_counter *counter_obj;
> + unsigned counterIndex;
> + unsigned i;
> +
> + group_obj = get_group(ctx, queryid_to_index(queryId));
> +
> /* "If the pair of queryId and counterId does not reference a valid counter,
> * an INVALID_VALUE error is generated."
> - *
> - * No queries are supported, so all queries are invalid.
> */
> + 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 anyway, no way for the
> + * application to know if the buffer was large enough.
> + */
> + 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 anyway, no way for the
> + * application to know if the buffer was large enough.
> + */
> + 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;
> + }
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfCounterInfoINTEL(invalid counterId)");
> + 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.
> + */
> +
> + /* "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;
> +
> + /* 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);
>
> /* "If queryId does not reference a valid query type,
> * an INVALID_VALUE error is generated."
> - *
> - * No queries are supported, so all queries are invalid.
> */
> + 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) {
> + /* "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);
> + _mesa_HashInsert(ctx->PerfMonitor.Monitors, first, m);
> + *queryHandle = first;
> +
> + ctx->Driver.ResetPerfMonitor(ctx, m);
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glCreatePerfQueryINTEL(invalid queryId)");
> + 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);
>
> /* "If a query handle doesn't reference a previously created performance
> * query instance, an INVALID_VALUE error is generated."
> - *
> - * No queries are supported, so all queries are invalid.
> */
> + if (m == NULL) {
> + _mesa_error(ctx, GL_INVALID_VALUE,
> + "glDeletePerfQueryINTEL(invalid queryHandle)");
> + return;
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfQueryINTEL(invalid queryHandle)");
> + /* 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);
>
> /* "If a query handle doesn't reference a previously created performance
> * query instance, an INVALID_VALUE error is generated."
> - *
> - * No queries are supported, so all queries are invalid.
> */
> + if (m == NULL) {
> + _mesa_error(ctx, GL_INVALID_VALUE,
> + "glBeginPerfQueryINTEL(invalid queryHandle)");
> + return;
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glBeginPerfQueryINTEL(invalid queryHandle)");
> + /* "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);
>
> /* "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.
> - *
> - * No queries are supported, so all handles are invalid.
> + * 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);
>
> - _mesa_error(ctx, GL_INVALID_OPERATION, "glEndPerfQueryINTEL(query not started)");
> + m->Active = false;
> + m->Ended = true;
> }
>
> extern void GLAPIENTRY
> @@ -800,15 +1151,24 @@ _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;
>
> /* "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)");
> + _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
> @@ -816,9 +1176,51 @@ _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
> *
> * 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 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."
> *
> - * No queries are supported, so all handles are invalid.
> + * If EndPerfQueryINTEL() is not called at all, we follow this.
> */
> + if (!m->Ended) {
> + *bytesWritten = 0;
> + return;
> + }
> +
> + result_available = ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
>
> - _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfQueryDataINTEL(invalid queryHandle)");
> + 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;
> + }
> }
>
More information about the mesa-dev
mailing list