[Mesa-dev] [PATCH 5/6] mesa: Implement INTEL_performance_query.
Ian Romanick
idr at freedesktop.org
Fri Apr 18 14:34:36 PDT 2014
On 03/17/2014 01:43 AM, Petri Latvala wrote:
> Using the existing driver hooks made for AMD_performance_monitor, implement
> INTEL_performance_query functions.
>
> v2: Whitespace changes.
With the formatting nits mentioned below fixed, this patch is
Reviewed-by: Ian Romanick <ian.d.romanick at intel.com>
> Signed-off-by: Petri Latvala <petri.latvala at intel.com>
> ---
> src/mesa/main/performance_monitor.c | 472 ++++++++++++++++++++++++++++++++----
> 1 file changed, 431 insertions(+), 41 deletions(-)
>
> diff --git a/src/mesa/main/performance_monitor.c b/src/mesa/main/performance_monitor.c
> index dd22fef..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,6 +685,8 @@ _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
> {
> GET_CURRENT_CONTEXT(ctx);
>
> + unsigned numGroups;
> +
> /* "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
> */
> if (!queryId) {
> @@ -653,13 +695,19 @@ _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
> 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
> @@ -678,14 +726,25 @@ _mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId)
>
> /* "If the specified performance
> * query identifier is invalid then INVALID_VALUE error is generated."
> - *
> - * No queries are supported, so all queries are invalid.
> - *
> - * "Whenever error is generated, the value of 0 is returned."
> */
> - *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." */
When the spec quotes in the previous patch are reformatted, this
comment can stay in the spec quote block. In fact, considering the
comment below, I'd be tempted to make the previous spec quote block:
/* 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."
*/
> + *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
> @@ -693,10 +752,32 @@ _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. */
True, but it's probably not what the application meant to do, either.
Maybe _mesa_warning?
> + 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,
> @@ -711,15 +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));
I'd be inclined to squish these two together. You can also constify
the pointer if you're that sort of person.
> +
> + 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.
> + */
Ugh... there's no way to ask the length of the longest name (like you
can do with program uniforms)? Bad spec, bad!
> + 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
> @@ -731,78 +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;
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE,
> - "glGetPerfCounterInfoINTEL(invalid counterId)");
> + 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);
Add a blank line here.
> + /* 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;
> + }
> + }
> +
> + 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;
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE,
> - "glCreatePerfQueryINTEL(invalid queryId)");
> + /* 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);
> +
> + 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 *
Extra * at the end of the line.
> + * 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;
> + }
Add a blank line here.
> + 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
> @@ -810,6 +1151,8 @@ _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."
> @@ -820,6 +1163,12 @@ _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
> 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
> @@ -827,10 +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;
> + }
>
> - _mesa_error(ctx, GL_INVALID_VALUE,
> - "glGetPerfQueryDataINTEL(invalid queryHandle)");
> + 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;
> + }
> }
>
More information about the mesa-dev
mailing list