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 ctx
->Driver
.DeletePerfQuery(ctx
, m
);
55 _mesa_free_performance_queries(struct gl_context
*ctx
)
57 _mesa_HashDeleteAll(ctx
->PerfQuery
.Objects
,
58 free_performance_query
, ctx
);
59 _mesa_DeleteHashTable(ctx
->PerfQuery
.Objects
);
62 static inline struct gl_perf_query_object
*
63 lookup_object(struct gl_context
*ctx
, GLuint id
)
65 return _mesa_HashLookup(ctx
->PerfQuery
.Objects
, id
);
69 init_performance_query_info(struct gl_context
*ctx
)
71 if (ctx
->Driver
.InitPerfQueryInfo
)
72 return ctx
->Driver
.InitPerfQueryInfo(ctx
);
77 /* For INTEL_performance_query, query id 0 is reserved to be invalid. */
78 static inline unsigned
79 queryid_to_index(GLuint queryid
)
85 index_to_queryid(unsigned index
)
91 queryid_valid(const struct gl_context
*ctx
, unsigned numQueries
, GLuint queryid
)
93 /* The GL_INTEL_performance_query spec says:
95 * "Performance counter ids values start with 1. Performance counter id 0
96 * is reserved as an invalid counter."
98 return queryid
!= 0 && queryid_to_index(queryid
) < numQueries
;
102 counterid_to_index(GLuint counterid
)
104 return counterid
- 1;
108 output_clipped_string(GLchar
*stringRet
,
115 strncpy(stringRet
, string
? string
: "", stringMaxLen
);
117 /* No specification given about whether returned strings needs
118 * to be zero-terminated. Zero-terminate the string always as we
119 * don't otherwise communicate the length of the returned
122 if (stringMaxLen
> 0)
123 stringRet
[stringMaxLen
- 1] = '\0';
126 /*****************************************************************************/
128 extern void GLAPIENTRY
129 _mesa_GetFirstPerfQueryIdINTEL(GLuint
*queryId
)
131 GET_CURRENT_CONTEXT(ctx
);
135 /* The GL_INTEL_performance_query spec says:
137 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
140 _mesa_error(ctx
, GL_INVALID_VALUE
,
141 "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
145 numQueries
= init_performance_query_info(ctx
);
147 /* The GL_INTEL_performance_query spec says:
149 * "If the given hardware platform doesn't support any performance
150 * queries, then the value of 0 is returned and INVALID_OPERATION error
153 if (numQueries
== 0) {
155 _mesa_error(ctx
, GL_INVALID_OPERATION
,
156 "glGetFirstPerfQueryIdINTEL(no queries supported)");
160 *queryId
= index_to_queryid(0);
163 extern void GLAPIENTRY
164 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId
, GLuint
*nextQueryId
)
166 GET_CURRENT_CONTEXT(ctx
);
170 /* The GL_INTEL_performance_query spec says:
172 * "The result is passed in location pointed by nextQueryId. If query
173 * identified by queryId is the last query available the value of 0 is
174 * returned. If the specified performance query identifier is invalid
175 * then INVALID_VALUE error is generated. If nextQueryId pointer is
176 * equal to 0, an INVALID_VALUE error is generated. Whenever error is
177 * generated, the value of 0 is returned."
181 _mesa_error(ctx
, GL_INVALID_VALUE
,
182 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
186 numQueries
= init_performance_query_info(ctx
);
188 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
189 _mesa_error(ctx
, GL_INVALID_VALUE
,
190 "glGetNextPerfQueryIdINTEL(invalid query)");
194 if (queryid_valid(ctx
, numQueries
, ++queryId
))
195 *nextQueryId
= queryId
;
200 extern void GLAPIENTRY
201 _mesa_GetPerfQueryIdByNameINTEL(char *queryName
, GLuint
*queryId
)
203 GET_CURRENT_CONTEXT(ctx
);
208 /* The GL_INTEL_performance_query spec says:
210 * "If queryName does not reference a valid query name, an INVALID_VALUE
211 * error is generated."
214 _mesa_error(ctx
, GL_INVALID_VALUE
,
215 "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
219 /* The specification does not state that this produces an error but
220 * to be consistent with glGetFirstPerfQueryIdINTEL we generate an
221 * INVALID_VALUE error
224 _mesa_error(ctx
, GL_INVALID_VALUE
,
225 "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
229 numQueries
= init_performance_query_info(ctx
);
231 for (i
= 0; i
< numQueries
; ++i
) {
235 ctx
->Driver
.GetPerfQueryInfo(ctx
, i
, &name
, &ignore
, &ignore
, &ignore
);
237 if (strcmp(name
, queryName
) == 0) {
238 *queryId
= index_to_queryid(i
);
243 _mesa_error(ctx
, GL_INVALID_VALUE
,
244 "glGetPerfQueryIdByNameINTEL(invalid query name)");
247 extern void GLAPIENTRY
248 _mesa_GetPerfQueryInfoINTEL(GLuint queryId
,
249 GLuint nameLength
, GLchar
*name
,
255 GET_CURRENT_CONTEXT(ctx
);
257 unsigned numQueries
= init_performance_query_info(ctx
);
258 unsigned queryIndex
= queryid_to_index(queryId
);
259 const char *queryName
;
260 GLuint queryDataSize
;
261 GLuint queryNumCounters
;
262 GLuint queryNumActive
;
264 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
265 /* The GL_INTEL_performance_query spec says:
267 * "If queryId does not reference a valid query type, an
268 * INVALID_VALUE error is generated."
270 _mesa_error(ctx
, GL_INVALID_VALUE
,
271 "glGetPerfQueryInfoINTEL(invalid query)");
275 ctx
->Driver
.GetPerfQueryInfo(ctx
, queryIndex
,
281 output_clipped_string(name
, nameLength
, queryName
);
284 *dataSize
= queryDataSize
;
287 *numCounters
= queryNumCounters
;
289 /* The GL_INTEL_performance_query spec says:
291 * "-- the actual number of already created query instances in
292 * maxInstances location"
294 * 1) Typo in the specification, should be noActiveInstances.
295 * 2) Another typo in the specification, maxInstances parameter is not listed
296 * in the declaration of this function in the list of new functions.
299 *numActive
= queryNumActive
;
301 /* Assume for now that all queries are per-context */
303 *capsMask
= GL_PERFQUERY_SINGLE_CONTEXT_INTEL
;
306 extern void GLAPIENTRY
307 _mesa_GetPerfCounterInfoINTEL(GLuint queryId
, GLuint counterId
,
308 GLuint nameLength
, GLchar
*name
,
309 GLuint descLength
, GLchar
*desc
,
313 GLuint
*dataTypeEnum
,
314 GLuint64
*rawCounterMaxValue
)
316 GET_CURRENT_CONTEXT(ctx
);
318 unsigned numQueries
= init_performance_query_info(ctx
);
319 unsigned queryIndex
= queryid_to_index(queryId
);
320 const char *queryName
;
321 GLuint queryDataSize
;
322 GLuint queryNumCounters
;
323 GLuint queryNumActive
;
324 unsigned counterIndex
;
325 const char *counterName
;
326 const char *counterDesc
;
327 GLuint counterOffset
;
328 GLuint counterDataSize
;
329 GLuint counterTypeEnum
;
330 GLuint counterDataTypeEnum
;
331 GLuint64 counterRawMax
;
333 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
334 /* The GL_INTEL_performance_query spec says:
336 * "If the pair of queryId and counterId does not reference a valid
337 * counter, an INVALID_VALUE error is generated."
339 _mesa_error(ctx
, GL_INVALID_VALUE
,
340 "glGetPerfCounterInfoINTEL(invalid queryId)");
344 ctx
->Driver
.GetPerfQueryInfo(ctx
, queryIndex
,
350 counterIndex
= counterid_to_index(counterId
);
352 if (counterIndex
>= queryNumCounters
) {
353 _mesa_error(ctx
, GL_INVALID_VALUE
,
354 "glGetPerfCounterInfoINTEL(invalid counterId)");
358 ctx
->Driver
.GetPerfCounterInfo(ctx
, queryIndex
, counterIndex
,
364 &counterDataTypeEnum
,
367 output_clipped_string(name
, nameLength
, counterName
);
368 output_clipped_string(desc
, descLength
, counterDesc
);
371 *offset
= counterOffset
;
374 *dataSize
= counterDataSize
;
377 *typeEnum
= counterTypeEnum
;
380 *dataTypeEnum
= counterDataTypeEnum
;
382 if (rawCounterMaxValue
)
383 *rawCounterMaxValue
= counterRawMax
;
385 if (rawCounterMaxValue
) {
386 /* The GL_INTEL_performance_query spec says:
388 * "for some raw counters for which the maximal value is
389 * deterministic, the maximal value of the counter in 1 second is
390 * returned in the location pointed by rawCounterMaxValue, otherwise,
391 * the location is written with the value of 0."
393 * Since it's very useful to be able to report a maximum value for
394 * more that just counters using the _COUNTER_RAW_INTEL or
395 * _COUNTER_DURATION_RAW_INTEL enums (e.g. for a _THROUGHPUT tools
396 * want to be able to visualize the absolute throughput with respect
397 * to the theoretical maximum that's possible) and there doesn't seem
398 * to be any reason not to allow _THROUGHPUT counters to also be
399 * considerer "raw" here, we always leave it up to the backend to
400 * decide when it's appropriate to report a maximum counter value or 0
403 *rawCounterMaxValue
= counterRawMax
;
407 extern void GLAPIENTRY
408 _mesa_CreatePerfQueryINTEL(GLuint queryId
, GLuint
*queryHandle
)
410 GET_CURRENT_CONTEXT(ctx
);
412 unsigned numQueries
= init_performance_query_info(ctx
);
414 struct gl_perf_query_object
*obj
;
416 /* The GL_INTEL_performance_query spec says:
418 * "If queryId does not reference a valid query type, an INVALID_VALUE
419 * error is generated."
421 if (!queryid_valid(ctx
, numQueries
, queryId
)) {
422 _mesa_error(ctx
, GL_INVALID_VALUE
,
423 "glCreatePerfQueryINTEL(invalid queryId)");
427 /* This is not specified in the extension, but is the only sane thing to
430 if (queryHandle
== NULL
) {
431 _mesa_error(ctx
, GL_INVALID_VALUE
,
432 "glCreatePerfQueryINTEL(queryHandle == NULL)");
436 id
= _mesa_HashFindFreeKeyBlock(ctx
->PerfQuery
.Objects
, 1);
438 /* The GL_INTEL_performance_query spec says:
440 * "If the query instance cannot be created due to exceeding the
441 * number of allowed instances or driver fails query creation due to
442 * an insufficient memory reason, an OUT_OF_MEMORY error is
443 * generated, and the location pointed by queryHandle returns NULL."
445 _mesa_error_no_memory(__func__
);
449 obj
= ctx
->Driver
.NewPerfQueryObject(ctx
, queryid_to_index(queryId
));
451 _mesa_error_no_memory(__func__
);
459 _mesa_HashInsert(ctx
->PerfQuery
.Objects
, id
, obj
);
463 extern void GLAPIENTRY
464 _mesa_DeletePerfQueryINTEL(GLuint queryHandle
)
466 GET_CURRENT_CONTEXT(ctx
);
468 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
470 /* The GL_INTEL_performance_query spec says:
472 * "If a query handle doesn't reference a previously created performance
473 * query instance, an INVALID_VALUE error is generated."
476 _mesa_error(ctx
, GL_INVALID_VALUE
,
477 "glDeletePerfQueryINTEL(invalid queryHandle)");
481 /* To avoid complications in the backend we never ask the backend to
482 * delete an active query or a query object while we are still
487 _mesa_EndPerfQueryINTEL(queryHandle
);
489 if (obj
->Used
&& !obj
->Ready
) {
490 ctx
->Driver
.WaitPerfQuery(ctx
, obj
);
494 _mesa_HashRemove(ctx
->PerfQuery
.Objects
, queryHandle
);
495 ctx
->Driver
.DeletePerfQuery(ctx
, obj
);
498 extern void GLAPIENTRY
499 _mesa_BeginPerfQueryINTEL(GLuint queryHandle
)
501 GET_CURRENT_CONTEXT(ctx
);
503 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
505 /* The GL_INTEL_performance_query spec says:
507 * "If a query handle doesn't reference a previously created performance
508 * query instance, an INVALID_VALUE error is generated."
511 _mesa_error(ctx
, GL_INVALID_VALUE
,
512 "glBeginPerfQueryINTEL(invalid queryHandle)");
516 /* The GL_INTEL_performance_query spec says:
518 * "Note that some query types, they cannot be collected in the same
519 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
520 * they refer to queries of such different types. In such case
521 * INVALID_OPERATION error is generated."
523 * We also generate an INVALID_OPERATION error if the driver can't begin
524 * a query for its own reasons, and for nesting the same query.
527 _mesa_error(ctx
, GL_INVALID_OPERATION
,
528 "glBeginPerfQueryINTEL(already active)");
532 /* To avoid complications in the backend we never ask the backend to
533 * reuse a query object and begin a new query while we are still
534 * waiting for data on that object.
536 if (obj
->Used
&& !obj
->Ready
) {
537 ctx
->Driver
.WaitPerfQuery(ctx
, obj
);
541 if (ctx
->Driver
.BeginPerfQuery(ctx
, obj
)) {
546 _mesa_error(ctx
, GL_INVALID_OPERATION
,
547 "glBeginPerfQueryINTEL(driver unable to begin query)");
551 extern void GLAPIENTRY
552 _mesa_EndPerfQueryINTEL(GLuint queryHandle
)
554 GET_CURRENT_CONTEXT(ctx
);
556 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
558 /* Not explicitly covered in the spec, but for consistency... */
560 _mesa_error(ctx
, GL_INVALID_VALUE
,
561 "glEndPerfQueryINTEL(invalid queryHandle)");
565 /* The GL_INTEL_performance_query spec says:
567 * "If a performance query is not currently started, an
568 * INVALID_OPERATION error will be generated."
572 _mesa_error(ctx
, GL_INVALID_OPERATION
,
573 "glEndPerfQueryINTEL(not active)");
577 ctx
->Driver
.EndPerfQuery(ctx
, obj
);
583 extern void GLAPIENTRY
584 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle
, GLuint flags
,
585 GLsizei dataSize
, void *data
, GLuint
*bytesWritten
)
587 GET_CURRENT_CONTEXT(ctx
);
589 struct gl_perf_query_object
*obj
= lookup_object(ctx
, queryHandle
);
591 /* Not explicitly covered in the spec, but for consistency... */
593 _mesa_error(ctx
, GL_INVALID_VALUE
,
594 "glEndPerfQueryINTEL(invalid queryHandle)");
598 /* The GL_INTEL_performance_query spec says:
600 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE
601 * error is generated."
603 if (!bytesWritten
|| !data
) {
604 _mesa_error(ctx
, GL_INVALID_VALUE
,
605 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
609 /* Just for good measure in case a lazy application is only
610 * checking this and not checking for errors...
614 /* Not explicitly covered in the spec but to be consistent with
615 * EndPerfQuery which validates that an application only ends an
616 * active query we also validate that an application doesn't try
617 * and get the data for a still active query...
620 _mesa_error(ctx
, GL_INVALID_OPERATION
,
621 "glGetPerfQueryDataINTEL(query still active)");
625 obj
->Ready
= ctx
->Driver
.IsPerfQueryReady(ctx
, obj
);
628 if (flags
== GL_PERFQUERY_FLUSH_INTEL
) {
629 ctx
->Driver
.Flush(ctx
);
630 } else if (flags
== GL_PERFQUERY_WAIT_INTEL
) {
631 ctx
->Driver
.WaitPerfQuery(ctx
, obj
);
637 ctx
->Driver
.GetPerfQueryData(ctx
, obj
, dataSize
, data
, bytesWritten
);