2 * Copyright © 2012 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_monitor.c
26 * Core Mesa support for the AMD_performance_monitor extension.
28 * In order to implement this extension, start by defining two enums:
29 * one for Groups, and one for Counters. These will be used as indexes into
30 * arrays, so they should start at 0 and increment from there.
32 * Counter IDs need to be globally unique. That is, you can't have counter 7
33 * in group A and counter 7 in group B. A global enum of all available
34 * counters is a convenient way to guarantee this.
44 #include "performance_monitor.h"
45 #include "util/bitset.h"
46 #include "util/ralloc.h"
49 _mesa_init_performance_monitors(struct gl_context
*ctx
)
51 ctx
->PerfMonitor
.Monitors
= _mesa_NewHashTable();
52 ctx
->PerfMonitor
.NumGroups
= 0;
53 ctx
->PerfMonitor
.Groups
= NULL
;
57 init_groups(struct gl_context
*ctx
)
59 if (unlikely(!ctx
->PerfMonitor
.Groups
))
60 ctx
->Driver
.InitPerfMonitorGroups(ctx
);
63 static struct gl_perf_monitor_object
*
64 new_performance_monitor(struct gl_context
*ctx
, GLuint index
)
67 struct gl_perf_monitor_object
*m
= ctx
->Driver
.NewPerfMonitor(ctx
);
77 rzalloc_array(NULL
, unsigned, ctx
->PerfMonitor
.NumGroups
);
80 ralloc_array(NULL
, BITSET_WORD
*, ctx
->PerfMonitor
.NumGroups
);
82 if (m
->ActiveGroups
== NULL
|| m
->ActiveCounters
== NULL
)
85 for (i
= 0; i
< ctx
->PerfMonitor
.NumGroups
; i
++) {
86 const struct gl_perf_monitor_group
*g
= &ctx
->PerfMonitor
.Groups
[i
];
88 m
->ActiveCounters
[i
] = rzalloc_array(m
->ActiveCounters
, BITSET_WORD
,
89 BITSET_WORDS(g
->NumCounters
));
90 if (m
->ActiveCounters
[i
] == NULL
)
97 ralloc_free(m
->ActiveGroups
);
98 ralloc_free(m
->ActiveCounters
);
99 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
104 free_performance_monitor(GLuint key
, void *data
, void *user
)
106 struct gl_perf_monitor_object
*m
= data
;
107 struct gl_context
*ctx
= user
;
109 ralloc_free(m
->ActiveGroups
);
110 ralloc_free(m
->ActiveCounters
);
111 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
115 _mesa_free_performance_monitors(struct gl_context
*ctx
)
117 _mesa_HashDeleteAll(ctx
->PerfMonitor
.Monitors
,
118 free_performance_monitor
, ctx
);
119 _mesa_DeleteHashTable(ctx
->PerfMonitor
.Monitors
);
122 static inline struct gl_perf_monitor_object
*
123 lookup_monitor(struct gl_context
*ctx
, GLuint id
)
125 return (struct gl_perf_monitor_object
*)
126 _mesa_HashLookup(ctx
->PerfMonitor
.Monitors
, id
);
129 static inline const struct gl_perf_monitor_group
*
130 get_group(const struct gl_context
*ctx
, GLuint id
)
132 if (id
>= ctx
->PerfMonitor
.NumGroups
)
135 return &ctx
->PerfMonitor
.Groups
[id
];
138 static inline const struct gl_perf_monitor_counter
*
139 get_counter(const struct gl_perf_monitor_group
*group_obj
, GLuint id
)
141 if (id
>= group_obj
->NumCounters
)
144 return &group_obj
->Counters
[id
];
147 /* For INTEL_performance_query, query id 0 is reserved to be invalid. We use
148 * index to Groups array + 1 as the query id. Same applies to counter id.
151 queryid_to_index(GLuint queryid
)
157 index_to_queryid(GLuint index
)
163 queryid_valid(const struct gl_context
*ctx
, GLuint queryid
)
165 return get_group(ctx
, queryid_to_index(queryid
)) != NULL
;
169 counterid_to_index(GLuint counterid
)
171 return counterid
- 1;
174 /*****************************************************************************/
177 _mesa_GetPerfMonitorGroupsAMD(GLint
*numGroups
, GLsizei groupsSize
,
180 GET_CURRENT_CONTEXT(ctx
);
183 if (numGroups
!= NULL
)
184 *numGroups
= ctx
->PerfMonitor
.NumGroups
;
186 if (groupsSize
> 0 && groups
!= NULL
) {
188 unsigned n
= MIN2((GLuint
) groupsSize
, ctx
->PerfMonitor
.NumGroups
);
190 /* We just use the index in the Groups array as the ID. */
191 for (i
= 0; i
< n
; i
++)
197 _mesa_GetPerfMonitorCountersAMD(GLuint group
, GLint
*numCounters
,
198 GLint
*maxActiveCounters
,
199 GLsizei countersSize
, GLuint
*counters
)
201 GET_CURRENT_CONTEXT(ctx
);
202 const struct gl_perf_monitor_group
*group_obj
;
206 group_obj
= get_group(ctx
, group
);
207 if (group_obj
== NULL
) {
208 _mesa_error(ctx
, GL_INVALID_VALUE
,
209 "glGetPerfMonitorCountersAMD(invalid group)");
213 if (maxActiveCounters
!= NULL
)
214 *maxActiveCounters
= group_obj
->MaxActiveCounters
;
216 if (numCounters
!= NULL
)
217 *numCounters
= group_obj
->NumCounters
;
219 if (counters
!= NULL
) {
221 unsigned n
= MIN2(group_obj
->NumCounters
, (GLuint
) countersSize
);
222 for (i
= 0; i
< n
; i
++) {
223 /* We just use the index in the Counters array as the ID. */
230 _mesa_GetPerfMonitorGroupStringAMD(GLuint group
, GLsizei bufSize
,
231 GLsizei
*length
, GLchar
*groupString
)
233 GET_CURRENT_CONTEXT(ctx
);
234 const struct gl_perf_monitor_group
*group_obj
;
238 group_obj
= get_group(ctx
, group
);
239 if (group_obj
== NULL
) {
240 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGetPerfMonitorGroupStringAMD");
245 /* Return the number of characters that would be required to hold the
246 * group string, excluding the null terminator.
249 *length
= strlen(group_obj
->Name
);
252 *length
= MIN2(strlen(group_obj
->Name
), bufSize
);
253 if (groupString
!= NULL
)
254 strncpy(groupString
, group_obj
->Name
, bufSize
);
259 _mesa_GetPerfMonitorCounterStringAMD(GLuint group
, GLuint counter
,
260 GLsizei bufSize
, GLsizei
*length
,
261 GLchar
*counterString
)
263 GET_CURRENT_CONTEXT(ctx
);
265 const struct gl_perf_monitor_group
*group_obj
;
266 const struct gl_perf_monitor_counter
*counter_obj
;
270 group_obj
= get_group(ctx
, group
);
272 if (group_obj
== NULL
) {
273 _mesa_error(ctx
, GL_INVALID_VALUE
,
274 "glGetPerfMonitorCounterStringAMD(invalid group)");
278 counter_obj
= get_counter(group_obj
, counter
);
280 if (counter_obj
== NULL
) {
281 _mesa_error(ctx
, GL_INVALID_VALUE
,
282 "glGetPerfMonitorCounterStringAMD(invalid counter)");
287 /* Return the number of characters that would be required to hold the
288 * counter string, excluding the null terminator.
291 *length
= strlen(counter_obj
->Name
);
294 *length
= MIN2(strlen(counter_obj
->Name
), bufSize
);
295 if (counterString
!= NULL
)
296 strncpy(counterString
, counter_obj
->Name
, bufSize
);
301 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group
, GLuint counter
, GLenum pname
,
304 GET_CURRENT_CONTEXT(ctx
);
306 const struct gl_perf_monitor_group
*group_obj
;
307 const struct gl_perf_monitor_counter
*counter_obj
;
311 group_obj
= get_group(ctx
, group
);
313 if (group_obj
== NULL
) {
314 _mesa_error(ctx
, GL_INVALID_VALUE
,
315 "glGetPerfMonitorCounterInfoAMD(invalid group)");
319 counter_obj
= get_counter(group_obj
, counter
);
321 if (counter_obj
== NULL
) {
322 _mesa_error(ctx
, GL_INVALID_VALUE
,
323 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
328 case GL_COUNTER_TYPE_AMD
:
329 *((GLenum
*) data
) = counter_obj
->Type
;
332 case GL_COUNTER_RANGE_AMD
:
333 switch (counter_obj
->Type
) {
335 case GL_PERCENTAGE_AMD
: {
336 float *f_data
= data
;
337 f_data
[0] = counter_obj
->Minimum
.f
;
338 f_data
[1] = counter_obj
->Maximum
.f
;
341 case GL_UNSIGNED_INT
: {
342 uint32_t *u32_data
= data
;
343 u32_data
[0] = counter_obj
->Minimum
.u32
;
344 u32_data
[1] = counter_obj
->Maximum
.u32
;
347 case GL_UNSIGNED_INT64_AMD
: {
348 uint64_t *u64_data
= data
;
349 u64_data
[0] = counter_obj
->Minimum
.u64
;
350 u64_data
[1] = counter_obj
->Maximum
.u64
;
354 assert(!"Should not get here: invalid counter type");
359 _mesa_error(ctx
, GL_INVALID_ENUM
,
360 "glGetPerfMonitorCounterInfoAMD(pname)");
366 _mesa_GenPerfMonitorsAMD(GLsizei n
, GLuint
*monitors
)
369 GET_CURRENT_CONTEXT(ctx
);
371 if (MESA_VERBOSE
& VERBOSE_API
)
372 _mesa_debug(ctx
, "glGenPerfMonitorsAMD(%d)\n", n
);
377 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenPerfMonitorsAMD(n < 0)");
381 if (monitors
== NULL
)
384 /* We don't actually need them to be contiguous, but this is what
385 * the rest of Mesa does, so we may as well.
387 first
= _mesa_HashFindFreeKeyBlock(ctx
->PerfMonitor
.Monitors
, n
);
390 for (i
= 0; i
< n
; i
++) {
391 struct gl_perf_monitor_object
*m
=
392 new_performance_monitor(ctx
, first
+ i
);
394 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenPerfMonitorsAMD");
397 monitors
[i
] = first
+ i
;
398 _mesa_HashInsert(ctx
->PerfMonitor
.Monitors
, first
+ i
, m
);
401 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenPerfMonitorsAMD");
407 _mesa_DeletePerfMonitorsAMD(GLsizei n
, GLuint
*monitors
)
410 GET_CURRENT_CONTEXT(ctx
);
412 if (MESA_VERBOSE
& VERBOSE_API
)
413 _mesa_debug(ctx
, "glDeletePerfMonitorsAMD(%d)\n", n
);
416 _mesa_error(ctx
, GL_INVALID_VALUE
, "glDeletePerfMonitorsAMD(n < 0)");
420 if (monitors
== NULL
)
423 for (i
= 0; i
< n
; i
++) {
424 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitors
[i
]);
427 /* Give the driver a chance to stop the monitor if it's active. */
429 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
433 _mesa_HashRemove(ctx
->PerfMonitor
.Monitors
, monitors
[i
]);
434 ralloc_free(m
->ActiveGroups
);
435 ralloc_free(m
->ActiveCounters
);
436 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
438 /* "INVALID_VALUE error will be generated if any of the monitor IDs
439 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
440 * reference a valid generated monitor ID."
442 _mesa_error(ctx
, GL_INVALID_VALUE
,
443 "glDeletePerfMonitorsAMD(invalid monitor)");
449 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor
, GLboolean enable
,
450 GLuint group
, GLint numCounters
,
453 GET_CURRENT_CONTEXT(ctx
);
455 struct gl_perf_monitor_object
*m
;
456 const struct gl_perf_monitor_group
*group_obj
;
458 m
= lookup_monitor(ctx
, monitor
);
460 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
461 * SelectPerfMonitorCountersAMD does not reference a monitor created by
462 * GenPerfMonitorsAMD."
465 _mesa_error(ctx
, GL_INVALID_VALUE
,
466 "glSelectPerfMonitorCountersAMD(invalid monitor)");
470 group_obj
= get_group(ctx
, group
);
472 /* "INVALID_VALUE error will be generated if the <group> parameter to
473 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
474 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
475 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
477 if (group_obj
== NULL
) {
478 _mesa_error(ctx
, GL_INVALID_VALUE
,
479 "glSelectPerfMonitorCountersAMD(invalid group)");
483 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
484 * SelectPerfMonitorCountersAMD is less than 0."
486 if (numCounters
< 0) {
487 _mesa_error(ctx
, GL_INVALID_VALUE
,
488 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
492 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
493 * results for that monitor become invalidated and the result queries
494 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
496 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
498 /* Sanity check the counter ID list. */
499 for (i
= 0; i
< numCounters
; i
++) {
500 if (counterList
[i
] >= group_obj
->NumCounters
) {
501 _mesa_error(ctx
, GL_INVALID_VALUE
,
502 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
508 /* Enable the counters */
509 for (i
= 0; i
< numCounters
; i
++) {
510 ++m
->ActiveGroups
[group
];
511 BITSET_SET(m
->ActiveCounters
[group
], counterList
[i
]);
514 /* Disable the counters */
515 for (i
= 0; i
< numCounters
; i
++) {
516 --m
->ActiveGroups
[group
];
517 BITSET_CLEAR(m
->ActiveCounters
[group
], counterList
[i
]);
523 _mesa_BeginPerfMonitorAMD(GLuint monitor
)
525 GET_CURRENT_CONTEXT(ctx
);
527 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
530 _mesa_error(ctx
, GL_INVALID_VALUE
,
531 "glBeginPerfMonitorAMD(invalid monitor)");
535 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
536 * called when a performance monitor is already active."
539 _mesa_error(ctx
, GL_INVALID_OPERATION
,
540 "glBeginPerfMonitor(already active)");
544 /* The driver is free to return false if it can't begin monitoring for
545 * any reason. This translates into an INVALID_OPERATION error.
547 if (ctx
->Driver
.BeginPerfMonitor(ctx
, m
)) {
551 _mesa_error(ctx
, GL_INVALID_OPERATION
,
552 "glBeginPerfMonitor(driver unable to begin monitoring)");
557 _mesa_EndPerfMonitorAMD(GLuint monitor
)
559 GET_CURRENT_CONTEXT(ctx
);
561 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
564 _mesa_error(ctx
, GL_INVALID_VALUE
, "glEndPerfMonitorAMD(invalid monitor)");
568 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
569 * when a performance monitor is not currently started."
572 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glBeginPerfMonitor(not active)");
576 ctx
->Driver
.EndPerfMonitor(ctx
, m
);
583 * Return the number of bytes needed to store a monitor's result.
586 perf_monitor_result_size(const struct gl_context
*ctx
,
587 const struct gl_perf_monitor_object
*m
)
589 unsigned group
, counter
;
592 for (group
= 0; group
< ctx
->PerfMonitor
.NumGroups
; group
++) {
593 const struct gl_perf_monitor_group
*g
= &ctx
->PerfMonitor
.Groups
[group
];
594 for (counter
= 0; counter
< g
->NumCounters
; counter
++) {
595 const struct gl_perf_monitor_counter
*c
= &g
->Counters
[counter
];
597 if (!BITSET_TEST(m
->ActiveCounters
[group
], counter
))
600 size
+= sizeof(uint32_t); /* Group ID */
601 size
+= sizeof(uint32_t); /* Counter ID */
602 size
+= _mesa_perf_monitor_counter_size(c
);
609 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor
, GLenum pname
,
610 GLsizei dataSize
, GLuint
*data
,
613 GET_CURRENT_CONTEXT(ctx
);
615 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
616 bool result_available
;
619 _mesa_error(ctx
, GL_INVALID_VALUE
,
620 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
624 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
626 _mesa_error(ctx
, GL_INVALID_OPERATION
,
627 "glGetPerfMonitorCounterDataAMD(data == NULL)");
631 /* We need at least enough room for a single value. */
632 if (dataSize
< sizeof(GLuint
)) {
633 if (bytesWritten
!= NULL
)
638 /* If the monitor has never ended, there is no result. */
639 result_available
= m
->Ended
&&
640 ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
642 /* AMD appears to return 0 for all queries unless a result is available. */
643 if (!result_available
) {
645 if (bytesWritten
!= NULL
)
646 *bytesWritten
= sizeof(GLuint
);
651 case GL_PERFMON_RESULT_AVAILABLE_AMD
:
653 if (bytesWritten
!= NULL
)
654 *bytesWritten
= sizeof(GLuint
);
656 case GL_PERFMON_RESULT_SIZE_AMD
:
657 *data
= perf_monitor_result_size(ctx
, m
);
658 if (bytesWritten
!= NULL
)
659 *bytesWritten
= sizeof(GLuint
);
661 case GL_PERFMON_RESULT_AMD
:
662 ctx
->Driver
.GetPerfMonitorResult(ctx
, m
, dataSize
, data
, bytesWritten
);
665 _mesa_error(ctx
, GL_INVALID_ENUM
,
666 "glGetPerfMonitorCounterDataAMD(pname)");
671 * Returns how many bytes a counter's value takes up.
674 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter
*c
)
678 case GL_PERCENTAGE_AMD
:
679 return sizeof(GLfloat
);
680 case GL_UNSIGNED_INT
:
681 return sizeof(GLuint
);
682 case GL_UNSIGNED_INT64_AMD
:
683 return sizeof(uint64_t);
685 assert(!"Should not get here: invalid counter type");
690 extern void GLAPIENTRY
691 _mesa_GetFirstPerfQueryIdINTEL(GLuint
*queryId
)
693 GET_CURRENT_CONTEXT(ctx
);
698 /* The GL_INTEL_performance_query spec says:
700 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
703 _mesa_error(ctx
, GL_INVALID_VALUE
,
704 "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
708 numGroups
= ctx
->PerfMonitor
.NumGroups
;
710 /* The GL_INTEL_performance_query spec says:
712 * "If the given hardware platform doesn't support any performance
713 * queries, then the value of 0 is returned and INVALID_OPERATION error
716 if (numGroups
== 0) {
718 _mesa_error(ctx
, GL_INVALID_OPERATION
,
719 "glGetFirstPerfQueryIdINTEL(no queries supported)");
723 *queryId
= index_to_queryid(0);
726 extern void GLAPIENTRY
727 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId
, GLuint
*nextQueryId
)
729 GET_CURRENT_CONTEXT(ctx
);
732 /* The GL_INTEL_performance_query spec says:
734 * "The result is passed in location pointed by nextQueryId. If query
735 * identified by queryId is the last query available the value of 0 is
736 * returned. If the specified performance query identifier is invalid
737 * then INVALID_VALUE error is generated. If nextQueryId pointer is
738 * equal to 0, an INVALID_VALUE error is generated. Whenever error is
739 * generated, the value of 0 is returned."
743 _mesa_error(ctx
, GL_INVALID_VALUE
,
744 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
748 if (!queryid_valid(ctx
, queryId
)) {
750 _mesa_error(ctx
, GL_INVALID_VALUE
,
751 "glGetNextPerfQueryIdINTEL(invalid query)");
757 if (!queryid_valid(ctx
, queryId
)) {
760 *nextQueryId
= queryId
;
764 extern void GLAPIENTRY
765 _mesa_GetPerfQueryIdByNameINTEL(char *queryName
, GLuint
*queryId
)
767 GET_CURRENT_CONTEXT(ctx
);
772 /* The GL_INTEL_performance_query spec says:
774 * "If queryName does not reference a valid query name, an INVALID_VALUE
775 * error is generated."
778 _mesa_error(ctx
, GL_INVALID_VALUE
,
779 "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
783 /* The specification does not state that this produces an error. */
785 _mesa_warning(ctx
, "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
789 for (i
= 0; i
< ctx
->PerfMonitor
.NumGroups
; ++i
) {
790 const struct gl_perf_monitor_group
*group_obj
= get_group(ctx
, i
);
791 if (strcmp(group_obj
->Name
, queryName
) == 0) {
792 *queryId
= index_to_queryid(i
);
797 _mesa_error(ctx
, GL_INVALID_VALUE
,
798 "glGetPerfQueryIdByNameINTEL(invalid query name)");
801 extern void GLAPIENTRY
802 _mesa_GetPerfQueryInfoINTEL(GLuint queryId
,
803 GLuint queryNameLength
, char *queryName
,
804 GLuint
*dataSize
, GLuint
*noCounters
,
805 GLuint
*noActiveInstances
,
808 GET_CURRENT_CONTEXT(ctx
);
811 const struct gl_perf_monitor_group
*group_obj
;
815 group_obj
= get_group(ctx
, queryid_to_index(queryId
));
816 if (group_obj
== NULL
) {
817 /* The GL_INTEL_performance_query spec says:
819 * "If queryId does not reference a valid query type, an
820 * INVALID_VALUE error is generated."
822 _mesa_error(ctx
, GL_INVALID_VALUE
,
823 "glGetPerfQueryInfoINTEL(invalid query)");
828 strncpy(queryName
, group_obj
->Name
, queryNameLength
);
830 /* No specification given about whether the string needs to be
831 * zero-terminated. Zero-terminate the string always as we don't
832 * otherwise communicate the length of the returned string.
834 if (queryNameLength
> 0) {
835 queryName
[queryNameLength
- 1] = '\0';
841 for (i
= 0; i
< group_obj
->NumCounters
; ++i
) {
842 /* What we get from the driver is group id (uint32_t) + counter id
843 * (uint32_t) + value.
845 size
+= 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj
->Counters
[i
]);
851 *noCounters
= group_obj
->NumCounters
;
854 /* The GL_INTEL_performance_query spec says:
856 * "-- the actual number of already created query instances in
857 * maxInstances location"
859 * 1) Typo in the specification, should be noActiveInstances.
860 * 2) Another typo in the specification, maxInstances parameter is not listed
861 * in the declaration of this function in the list of new functions.
863 if (noActiveInstances
) {
864 *noActiveInstances
= _mesa_HashNumEntries(ctx
->PerfMonitor
.Monitors
);
868 /* TODO: This information not yet available in the monitor structs. For
869 * now, we hardcode SINGLE_CONTEXT, since that's what the implementation
870 * currently tries very hard to do.
872 *capsMask
= GL_PERFQUERY_SINGLE_CONTEXT_INTEL
;
876 extern void GLAPIENTRY
877 _mesa_GetPerfCounterInfoINTEL(GLuint queryId
, GLuint counterId
,
878 GLuint counterNameLength
, char *counterName
,
879 GLuint counterDescLength
, char *counterDesc
,
880 GLuint
*counterOffset
, GLuint
*counterDataSize
, GLuint
*counterTypeEnum
,
881 GLuint
*counterDataTypeEnum
, GLuint64
*rawCounterMaxValue
)
883 GET_CURRENT_CONTEXT(ctx
);
885 const struct gl_perf_monitor_group
*group_obj
;
886 const struct gl_perf_monitor_counter
*counter_obj
;
887 unsigned counterIndex
;
892 group_obj
= get_group(ctx
, queryid_to_index(queryId
));
894 /* The GL_INTEL_performance_query spec says:
896 * "If the pair of queryId and counterId does not reference a valid
897 * counter, an INVALID_VALUE error is generated."
899 if (group_obj
== NULL
) {
900 _mesa_error(ctx
, GL_INVALID_VALUE
,
901 "glGetPerfCounterInfoINTEL(invalid queryId)");
905 counterIndex
= counterid_to_index(counterId
);
906 counter_obj
= get_counter(group_obj
, counterIndex
);
908 if (counter_obj
== NULL
) {
909 _mesa_error(ctx
, GL_INVALID_VALUE
,
910 "glGetPerfCounterInfoINTEL(invalid counterId)");
915 strncpy(counterName
, counter_obj
->Name
, counterNameLength
);
917 /* No specification given about whether the string needs to be
918 * zero-terminated. Zero-terminate the string always as we don't
919 * otherwise communicate the length of the returned string.
921 if (counterNameLength
> 0) {
922 counterName
[counterNameLength
- 1] = '\0';
927 /* TODO: No separate description text at the moment. We pass the name
928 * again for the moment.
930 strncpy(counterDesc
, counter_obj
->Name
, counterDescLength
);
932 /* No specification given about whether the string needs to be
933 * zero-terminated. Zero-terminate the string always as we don't
934 * otherwise communicate the length of the returned string.
936 if (counterDescLength
> 0) {
937 counterDesc
[counterDescLength
- 1] = '\0';
943 for (i
= 0; i
< counterIndex
; ++i
) {
944 /* What we get from the driver is group id (uint32_t) + counter id
945 * (uint32_t) + value.
947 offset
+= 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj
->Counters
[i
]);
949 *counterOffset
= 2 * sizeof(uint32_t) + offset
;
952 if (counterDataSize
) {
953 *counterDataSize
= _mesa_perf_monitor_counter_size(counter_obj
);
956 if (counterTypeEnum
) {
957 /* TODO: Different counter types (semantic type, not data type) not
958 * supported as of yet.
960 *counterTypeEnum
= GL_PERFQUERY_COUNTER_RAW_INTEL
;
963 if (counterDataTypeEnum
) {
964 switch (counter_obj
->Type
) {
966 case GL_PERCENTAGE_AMD
:
967 *counterDataTypeEnum
= GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL
;
969 case GL_UNSIGNED_INT
:
970 *counterDataTypeEnum
= GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL
;
972 case GL_UNSIGNED_INT64_AMD
:
973 *counterDataTypeEnum
= GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL
;
976 assert(!"Should not get here: invalid counter type");
981 if (rawCounterMaxValue
) {
982 /* This value is (implicitly) specified to be used only with
983 * GL_PERFQUERY_COUNTER_RAW_INTEL counters. When semantic types for
984 * counters are added, that needs to be checked.
987 /* The GL_INTEL_performance_query spec says:
989 * "for some raw counters for which the maximal value is
990 * deterministic, the maximal value of the counter in 1 second is
991 * returned in the location pointed by rawCounterMaxValue, otherwise,
992 * the location is written with the value of 0."
994 * The maximum value reported by the driver at the moment is not with
995 * these semantics, so write 0 always to be safe.
997 *rawCounterMaxValue
= 0;
1001 extern void GLAPIENTRY
1002 _mesa_CreatePerfQueryINTEL(GLuint queryId
, GLuint
*queryHandle
)
1004 GET_CURRENT_CONTEXT(ctx
);
1007 const struct gl_perf_monitor_group
*group_obj
;
1008 struct gl_perf_monitor_object
*m
;
1013 /* This is not specified in the extension, but is the only sane thing to
1016 if (queryHandle
== NULL
) {
1017 _mesa_error(ctx
, GL_INVALID_VALUE
,
1018 "glCreatePerfQueryINTEL(queryHandle == NULL)");
1022 group
= queryid_to_index(queryId
);
1023 group_obj
= get_group(ctx
, group
);
1025 /* The GL_INTEL_performance_query spec says:
1027 * "If queryId does not reference a valid query type, an INVALID_VALUE
1028 * error is generated."
1030 if (group_obj
== NULL
) {
1031 _mesa_error(ctx
, GL_INVALID_VALUE
,
1032 "glCreatePerfQueryINTEL(invalid queryId)");
1036 /* The query object created here is the counterpart of a `monitor' in
1037 * AMD_performance_monitor. This call is equivalent to calling
1038 * GenPerfMonitorsAMD and SelectPerfMonitorCountersAMD with a list of all
1039 * counters in a group.
1042 /* We keep the monitor ids contiguous */
1043 first
= _mesa_HashFindFreeKeyBlock(ctx
->PerfMonitor
.Monitors
, 1);
1045 /* The GL_INTEL_performance_query spec says:
1047 * "If the query instance cannot be created due to exceeding the
1048 * number of allowed instances or driver fails query creation due to
1049 * an insufficient memory reason, an OUT_OF_MEMORY error is
1050 * generated, and the location pointed by queryHandle returns NULL."
1052 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCreatePerfQueryINTEL");
1056 m
= new_performance_monitor(ctx
, first
);
1058 _mesa_error_no_memory(__func__
);
1062 _mesa_HashInsert(ctx
->PerfMonitor
.Monitors
, first
, m
);
1063 *queryHandle
= first
;
1065 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
1067 for (i
= 0; i
< group_obj
->NumCounters
; ++i
) {
1068 ++m
->ActiveGroups
[group
];
1069 /* Counters are a continuous range of integers, 0 to NumCounters (excl),
1070 * so i is the counter value to use here.
1072 BITSET_SET(m
->ActiveCounters
[group
], i
);
1076 extern void GLAPIENTRY
1077 _mesa_DeletePerfQueryINTEL(GLuint queryHandle
)
1079 GET_CURRENT_CONTEXT(ctx
);
1080 struct gl_perf_monitor_object
*m
;
1082 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1085 m
= lookup_monitor(ctx
, queryHandle
);
1087 /* The GL_INTEL_performance_query spec says:
1089 * "If a query handle doesn't reference a previously created performance
1090 * query instance, an INVALID_VALUE error is generated."
1093 _mesa_error(ctx
, GL_INVALID_VALUE
,
1094 "glDeletePerfQueryINTEL(invalid queryHandle)");
1098 /* Let the driver stop the monitor if it's active. */
1100 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
1104 _mesa_HashRemove(ctx
->PerfMonitor
.Monitors
, queryHandle
);
1105 ralloc_free(m
->ActiveGroups
);
1106 ralloc_free(m
->ActiveCounters
);
1107 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
1110 extern void GLAPIENTRY
1111 _mesa_BeginPerfQueryINTEL(GLuint queryHandle
)
1113 GET_CURRENT_CONTEXT(ctx
);
1114 struct gl_perf_monitor_object
*m
;
1116 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1120 m
= lookup_monitor(ctx
, queryHandle
);
1122 /* The GL_INTEL_performance_query spec says:
1124 * "If a query handle doesn't reference a previously created performance
1125 * query instance, an INVALID_VALUE error is generated."
1128 _mesa_error(ctx
, GL_INVALID_VALUE
,
1129 "glBeginPerfQueryINTEL(invalid queryHandle)");
1133 /* The GL_INTEL_performance_query spec says:
1135 * "Note that some query types, they cannot be collected in the same
1136 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
1137 * they refer to queries of such different types. In such case
1138 * INVALID_OPERATION error is generated."
1140 * We also generate an INVALID_OPERATION error if the driver can't begin
1141 * monitoring for its own reasons, and for nesting the same query.
1144 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1145 "glBeginPerfQueryINTEL(already active)");
1149 if (ctx
->Driver
.BeginPerfMonitor(ctx
, m
)) {
1153 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1154 "glBeginPerfQueryINTEL(driver unable to begin monitoring)");
1158 extern void GLAPIENTRY
1159 _mesa_EndPerfQueryINTEL(GLuint queryHandle
)
1161 GET_CURRENT_CONTEXT(ctx
);
1162 struct gl_perf_monitor_object
*m
;
1164 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1168 m
= lookup_monitor(ctx
, queryHandle
);
1170 /* The GL_INTEL_performance_query spec says:
1172 * "If a performance query is not currently started, an
1173 * INVALID_OPERATION error will be generated."
1175 * The specification doesn't state that an invalid handle would be an
1176 * INVALID_VALUE error. Regardless, query for such a handle will not be
1177 * started, so we generate an INVALID_OPERATION in that case too.
1180 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1181 "glEndPerfQueryINTEL(invalid queryHandle)");
1186 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1187 "glEndPerfQueryINTEL(not active)");
1191 ctx
->Driver
.EndPerfMonitor(ctx
, m
);
1197 extern void GLAPIENTRY
1198 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle
, GLuint flags
,
1199 GLsizei dataSize
, void *data
, GLuint
*bytesWritten
)
1201 GET_CURRENT_CONTEXT(ctx
);
1202 struct gl_perf_monitor_object
*m
;
1203 bool result_available
;
1205 /* The GL_INTEL_performance_query spec says:
1207 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE
1208 * error is generated."
1210 if (!bytesWritten
|| !data
) {
1211 _mesa_error(ctx
, GL_INVALID_VALUE
,
1212 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
1216 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1220 m
= lookup_monitor(ctx
, queryHandle
);
1222 /* The specification doesn't state that an invalid handle generates an
1223 * error. We could interpret that to mean the case should be handled as
1224 * "measurement not ready for this query", but what should be done if
1225 * `flags' equals PERFQUERY_WAIT_INTEL?
1227 * To resolve this, we just generate an INVALID_VALUE from an invalid query
1231 _mesa_error(ctx
, GL_INVALID_VALUE
,
1232 "glGetPerfQueryDataINTEL(invalid queryHandle)");
1236 /* We need at least enough room for a single value. */
1237 if (dataSize
< sizeof(GLuint
)) {
1242 /* The GL_INTEL_performance_query spec says:
1244 * "The call may end without returning any data if they are not ready
1245 * for reading as the measurement session is still pending (the
1246 * EndPerfQueryINTEL() command processing is not finished by
1247 * hardware). In this case location pointed by the bytesWritten
1248 * parameter will be set to 0."
1250 * If EndPerfQueryINTEL() is not called at all, we follow this.
1257 result_available
= ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
1259 if (!result_available
) {
1260 if (flags
== GL_PERFQUERY_FLUSH_INTEL
) {
1261 ctx
->Driver
.Flush(ctx
);
1262 } else if (flags
== GL_PERFQUERY_WAIT_INTEL
) {
1263 /* Assume Finish() is both enough and not too much to wait for
1264 * results. If results are still not available after Finish(), the
1265 * later code automatically bails out with 0 for bytesWritten.
1267 ctx
->Driver
.Finish(ctx
);
1269 ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
1273 if (result_available
) {
1274 ctx
->Driver
.GetPerfMonitorResult(ctx
, m
, dataSize
, data
, (GLint
*)bytesWritten
);