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.latv...@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; > + } > } > _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev