2 * Copyright © 2012-2017 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
25 * \file performance_query.c
26 * Core Mesa support for the INTEL_performance_query extension.
36 #include "performance_query.h"
37 #include "util/ralloc.h"
40 _mesa_init_performance_queries(struct gl_context
*ctx
)
42 ctx
->PerfQuery
.Objects
= _mesa_NewHashTable();
46 free_performance_query(GLuint key
, void *data
, void *user
)
48 struct gl_perf_query_object
*m
= data
;
49 struct gl_context
*ctx
= user
;
51 /* Don't confuse the implementation by deleting an active query. We can
52 * toggle Active/Used to false because we're tearing down the GL context
53 * and it's already idle (see _mesa_free_context_data).
57 ctx
->Driver
.DeletePerfQuery(ctx
, m
);
61 _mesa_free_performance_queries(struct gl_context
*ctx
)
63 _mesa_HashDeleteAll(ctx
->PerfQuery
.Objects
,
64 free_performance_query
, ctx
);
65 _mesa_DeleteHashTable(ctx
->PerfQuery
.Objects
);
68 static inline struct gl_perf_query_object
*
69 lookup_object(struct gl_context
*ctx
, GLuint id
)
71 return _mesa_HashLookup(ctx
->PerfQuery
.Objects
, id
);
75 init_performance_query_info(struct gl_context
*ctx
)
77 if (ctx
->Driver
.InitPerfQueryInfo
)
78 return ctx
->Driver
.InitPerfQueryInfo(ctx
);
83 /* For INTEL_performance_query, query id 0 is reserved to be invalid. */
84 static inline unsigned
85 queryid_to_index(GLuint queryid
)
91 index_to_queryid(unsigned index
)
97 queryid_valid(const struct gl_context
*ctx
, unsigned numQueries
, GLuint queryid
)
99 /* The GL_INTEL_performance_query spec says:
101 * "Performance counter ids values start with 1. Performance counter id 0
102 * is reserved as an invalid counter."
104 return queryid
!= 0 && queryid_to_index(queryid
) < numQueries
;
108 counterid_to_index(GLuint counterid
)
110 return counterid
- 1;
114 output_clipped_string(GLchar
*stringRet
,
121 strncpy(stringRet
, string
? string
: "", stringMaxLen
);
123 /* No specification given about whether returned strings needs
124 * to be zero-terminated. Zero-terminate the string always as we
125 * don't otherwise communicate the length of the returned
128 if (stringMaxLen
> 0)
129 stringRet
[stringMaxLen
- 1] = '\0';
132 /*****************************************************************************/
134 extern void GLAPIENTRY
135 _mesa_GetFirstPerfQueryIdINTEL(GLuint
*queryId
)
137 GET_CURRENT_CONTEXT(ctx
);
141 /* The GL_INTEL_performance_query spec says:
143 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
146 _mesa_error(ctx
, GL_INVALID_VALUE
,
147 "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
151 numQueries
= init_performance_query_info(ctx
);
153 /* The GL_INTEL_performance_query spec says:
155 * "If the given hardware platform doesn't support any performance
156 * queries, then the value of 0 is returned and INVALID_OPERATION error
159 if (numQueries
== 0) {
161 _mesa_error(ctx
, GL_INVALID_OPERATION
,
162 "glGetFirstPerfQueryIdINTEL(no queries supported)");
166 *queryId
= index_to_queryid(0);
169 extern void GLAPIENTRY
170 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId
, GLuint
*nextQueryId
)
172 GET_CURRENT_CONTEXT(ctx
);
176 /* The GL_INTEL_performance_query spec says:
178 * "The result is passed in location pointed by nextQueryId. If query
179 * identified by queryId is the last query available the value of 0 is
180 * returned. If the specified performance query identifier is invalid
181 * then INVALID_VALUE error is generated. If nextQueryId pointer is
182 * equal to 0, an INVALID_VALUE error is generated. Whenever error is
183 * generated, the value of 0 is returned."
187 _mesa_error(ctx
, GL_INVALID_VALUE
,
188 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
192 numQueries
= init_performance_query_info(ctx
);
194 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
195 _mesa_error(ctx
, GL_INVALID_VALUE
,
196 "glGetNextPerfQueryIdINTEL(invalid query)");
200 if (queryid_valid(ctx
, numQueries
, ++queryId
))
201 *nextQueryId
= queryId
;
206 extern void GLAPIENTRY
207 _mesa_GetPerfQueryIdByNameINTEL(char *queryName
, GLuint
*queryId
)
209 GET_CURRENT_CONTEXT(ctx
);
214 /* The GL_INTEL_performance_query spec says:
216 * "If queryName does not reference a valid query name, an INVALID_VALUE
217 * error is generated."
220 _mesa_error(ctx
, GL_INVALID_VALUE
,
221 "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
225 /* The specification does not state that this produces an error but
226 * to be consistent with glGetFirstPerfQueryIdINTEL we generate an
227 * INVALID_VALUE error
230 _mesa_error(ctx
, GL_INVALID_VALUE
,
231 "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
235 numQueries
= init_performance_query_info(ctx
);
237 for (i
= 0; i
< numQueries
; ++i
) {
241 ctx
->Driver
.GetPerfQueryInfo(ctx
, i
, &name
, &ignore
, &ignore
, &ignore
);
243 if (strcmp(name
, queryName
) == 0) {
244 *queryId
= index_to_queryid(i
);
249 _mesa_error(ctx
, GL_INVALID_VALUE
,
250 "glGetPerfQueryIdByNameINTEL(invalid query name)");
253 extern void GLAPIENTRY
254 _mesa_GetPerfQueryInfoINTEL(GLuint queryId
,
255 GLuint nameLength
, GLchar
*name
,
261 GET_CURRENT_CONTEXT(ctx
);
263 unsigned numQueries
= init_performance_query_info(ctx
);
264 unsigned queryIndex
= queryid_to_index(queryId
);
265 const char *queryName
;
266 GLuint queryDataSize
;
267 GLuint queryNumCounters
;
268 GLuint queryNumActive
;
270 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
271 /* The GL_INTEL_performance_query spec says:
273 * "If queryId does not reference a valid query type, an
274 * INVALID_VALUE error is generated."
276 _mesa_error(ctx
, GL_INVALID_VALUE
,
277 "glGetPerfQueryInfoINTEL(invalid query)");
281 ctx
->Driver
.GetPerfQueryInfo(ctx
, queryIndex
,
287 output_clipped_string(name
, nameLength
, queryName
);
290 *dataSize
= queryDataSize
;
293 *numCounters
= queryNumCounters
;
295 /* The GL_INTEL_performance_query spec says:
297 * "-- the actual number of already created query instances in
298 * maxInstances location"
300 * 1) Typo in the specification, should be noActiveInstances.
301 * 2) Another typo in the specification, maxInstances parameter is not listed
302 * in the declaration of this function in the list of new functions.
305 *numActive
= queryNumActive
;
307 /* Assume for now that all queries are per-context */
309 *capsMask
= GL_PERFQUERY_SINGLE_CONTEXT_INTEL
;
312 extern void GLAPIENTRY
313 _mesa_GetPerfCounterInfoINTEL(GLuint queryId
, GLuint counterId
,
314 GLuint nameLength
, GLchar
*name
,
315 GLuint descLength
, GLchar
*desc
,
319 GLuint
*dataTypeEnum
,
320 GLuint64
*rawCounterMaxValue
)
322 GET_CURRENT_CONTEXT(ctx
);
324 unsigned numQueries
= init_performance_query_info(ctx
);
325 unsigned queryIndex
= queryid_to_index(queryId
);
326 const char *queryName
;
327 GLuint queryDataSize
;
328 GLuint queryNumCounters
;
329 GLuint queryNumActive
;
330 unsigned counterIndex
;
331 const char *counterName
;
332 const char *counterDesc
;
333 GLuint counterOffset
;
334 GLuint counterDataSize
;
335 GLuint counterTypeEnum
;
336 GLuint counterDataTypeEnum
;
337 GLuint64 counterRawMax
;
339 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
340 /* The GL_INTEL_performance_query spec says:
342 * "If the pair of queryId and counterId does not reference a valid
343 * counter, an INVALID_VALUE error is generated."
345 _mesa_error(ctx
, GL_INVALID_VALUE
,
346 "glGetPerfCounterInfoINTEL(invalid queryId)");
350 ctx
->Driver
.GetPerfQueryInfo(ctx
, queryIndex
,
356 counterIndex
= counterid_to_index(counterId
);
358 if (counterIndex
>= queryNumCounters
) {
359 _mesa_error(ctx
, GL_INVALID_VALUE
,
360 "glGetPerfCounterInfoINTEL(invalid counterId)");
364 ctx
->Driver
.GetPerfCounterInfo(ctx
, queryIndex
, counterIndex
,
370 &counterDataTypeEnum
,
373 output_clipped_string(name
, nameLength
, counterName
);
374 output_clipped_string(desc
, descLength
, counterDesc
);
377 *offset
= counterOffset
;
380 *dataSize
= counterDataSize
;
383 *typeEnum
= counterTypeEnum
;
386 *dataTypeEnum
= counterDataTypeEnum
;
388 if (rawCounterMaxValue
)
389 *rawCounterMaxValue
= counterRawMax
;
391 if (rawCounterMaxValue
) {
392 /* The GL_INTEL_performance_query spec says:
394 * "for some raw counters for which the maximal value is
395 * deterministic, the maximal value of the counter in 1 second is
396 * returned in the location pointed by rawCounterMaxValue, otherwise,
397 * the location is written with the value of 0."
399 * Since it's very useful to be able to report a maximum value for
400 * more that just counters using the _COUNTER_RAW_INTEL or
401 * _COUNTER_DURATION_RAW_INTEL enums (e.g. for a _THROUGHPUT tools
402 * want to be able to visualize the absolute throughput with respect
403 * to the theoretical maximum that's possible) and there doesn't seem
404 * to be any reason not to allow _THROUGHPUT counters to also be
405 * considerer "raw" here, we always leave it up to the backend to
406 * decide when it's appropriate to report a maximum counter value or 0
409 *rawCounterMaxValue
= counterRawMax
;
413 extern void GLAPIENTRY
414 _mesa_CreatePerfQueryINTEL(GLuint queryId
, GLuint
*queryHandle
)
416 GET_CURRENT_CONTEXT(ctx
);
418 unsigned numQueries
= init_performance_query_info(ctx
);
420 struct gl_perf_query_object
*obj
;
422 /* The GL_INTEL_performance_query spec says:
424 * "If queryId does not reference a valid query type, an INVALID_VALUE
425 * error is generated."
427 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
428 _mesa_error(ctx
, GL_INVALID_VALUE
,
429 "glCreatePerfQueryINTEL(invalid queryId)");
433 /* This is not specified in the extension, but is the only sane thing to
436 if (queryHandle
== NULL
) {
437 _mesa_error(ctx
, GL_INVALID_VALUE
,
438 "glCreatePerfQueryINTEL(queryHandle == NULL)");
442 id
= _mesa_HashFindFreeKeyBlock(ctx
->PerfQuery
.Objects
, 1);
444 /* The GL_INTEL_performance_query spec says:
446 * "If the query instance cannot be created due to exceeding the
447 * number of allowed instances or driver fails query creation due to
448 * an insufficient memory reason, an OUT_OF_MEMORY error is
449 * generated, and the location pointed by queryHandle returns NULL."
451 _mesa_error_no_memory(__func__
);
455 obj
= ctx
->Driver
.NewPerfQueryObject(ctx
, queryid_to_index(queryId
));
457 _mesa_error_no_memory(__func__
);
465 _mesa_HashInsert(ctx
->PerfQuery
.Objects
, id
, obj
);
469 extern void GLAPIENTRY
470 _mesa_DeletePerfQueryINTEL(GLuint queryHandle
)
472 GET_CURRENT_CONTEXT(ctx
);
474 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
476 /* The GL_INTEL_performance_query spec says:
478 * "If a query handle doesn't reference a previously created performance
479 * query instance, an INVALID_VALUE error is generated."
482 _mesa_error(ctx
, GL_INVALID_VALUE
,
483 "glDeletePerfQueryINTEL(invalid queryHandle)");
487 /* To avoid complications in the backend we never ask the backend to
488 * delete an active query or a query object while we are still
493 _mesa_EndPerfQueryINTEL(queryHandle
);
495 if (obj
->Used
&& !obj
->Ready
) {
496 ctx
->Driver
.WaitPerfQuery(ctx
, obj
);
500 _mesa_HashRemove(ctx
->PerfQuery
.Objects
, queryHandle
);
501 ctx
->Driver
.DeletePerfQuery(ctx
, obj
);
504 extern void GLAPIENTRY
505 _mesa_BeginPerfQueryINTEL(GLuint queryHandle
)
507 GET_CURRENT_CONTEXT(ctx
);
509 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
511 /* The GL_INTEL_performance_query spec says:
513 * "If a query handle doesn't reference a previously created performance
514 * query instance, an INVALID_VALUE error is generated."
517 _mesa_error(ctx
, GL_INVALID_VALUE
,
518 "glBeginPerfQueryINTEL(invalid queryHandle)");
522 /* The GL_INTEL_performance_query spec says:
524 * "Note that some query types, they cannot be collected in the same
525 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
526 * they refer to queries of such different types. In such case
527 * INVALID_OPERATION error is generated."
529 * We also generate an INVALID_OPERATION error if the driver can't begin
530 * a query for its own reasons, and for nesting the same query.
533 _mesa_error(ctx
, GL_INVALID_OPERATION
,
534 "glBeginPerfQueryINTEL(already active)");
538 /* To avoid complications in the backend we never ask the backend to
539 * reuse a query object and begin a new query while we are still
540 * waiting for data on that object.
542 if (obj
->Used
&& !obj
->Ready
) {
543 ctx
->Driver
.WaitPerfQuery(ctx
, obj
);
547 if (ctx
->Driver
.BeginPerfQuery(ctx
, obj
)) {
552 _mesa_error(ctx
, GL_INVALID_OPERATION
,
553 "glBeginPerfQueryINTEL(driver unable to begin query)");
557 extern void GLAPIENTRY
558 _mesa_EndPerfQueryINTEL(GLuint queryHandle
)
560 GET_CURRENT_CONTEXT(ctx
);
562 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
564 /* Not explicitly covered in the spec, but for consistency... */
566 _mesa_error(ctx
, GL_INVALID_VALUE
,
567 "glEndPerfQueryINTEL(invalid queryHandle)");
571 /* The GL_INTEL_performance_query spec says:
573 * "If a performance query is not currently started, an
574 * INVALID_OPERATION error will be generated."
578 _mesa_error(ctx
, GL_INVALID_OPERATION
,
579 "glEndPerfQueryINTEL(not active)");
583 ctx
->Driver
.EndPerfQuery(ctx
, obj
);
589 extern void GLAPIENTRY
590 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle
, GLuint flags
,
591 GLsizei dataSize
, void *data
, GLuint
*bytesWritten
)
593 GET_CURRENT_CONTEXT(ctx
);
595 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
597 /* Not explicitly covered in the spec, but for consistency... */
599 _mesa_error(ctx
, GL_INVALID_VALUE
,
600 "glEndPerfQueryINTEL(invalid queryHandle)");
604 /* The GL_INTEL_performance_query spec says:
606 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE
607 * error is generated."
609 if (!bytesWritten
|| !data
) {
610 _mesa_error(ctx
, GL_INVALID_VALUE
,
611 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
615 /* Just for good measure in case a lazy application is only
616 * checking this and not checking for errors...
620 /* Not explicitly covered in the spec but to be consistent with
621 * EndPerfQuery which validates that an application only ends an
622 * active query we also validate that an application doesn't try
623 * and get the data for a still active query...
626 _mesa_error(ctx
, GL_INVALID_OPERATION
,
627 "glGetPerfQueryDataINTEL(query still active)");
631 obj
->Ready
= ctx
->Driver
.IsPerfQueryReady(ctx
, obj
);
634 if (flags
== GL_PERFQUERY_FLUSH_INTEL
) {
635 ctx
->Driver
.Flush(ctx
);
636 } else if (flags
== GL_PERFQUERY_WAIT_INTEL
) {
637 ctx
->Driver
.WaitPerfQuery(ctx
, obj
);
643 ctx
->Driver
.GetPerfQueryData(ctx
, obj
, dataSize
, data
, bytesWritten
);