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"
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
;
56 static struct gl_perf_monitor_object
*
57 new_performance_monitor(struct gl_context
*ctx
, GLuint index
)
60 struct gl_perf_monitor_object
*m
= ctx
->Driver
.NewPerfMonitor(ctx
);
70 rzalloc_array(NULL
, unsigned, ctx
->PerfMonitor
.NumGroups
);
73 ralloc_array(NULL
, BITSET_WORD
*, ctx
->PerfMonitor
.NumGroups
);
75 if (m
->ActiveGroups
== NULL
|| m
->ActiveCounters
== NULL
)
78 for (i
= 0; i
< ctx
->PerfMonitor
.NumGroups
; i
++) {
79 const struct gl_perf_monitor_group
*g
= &ctx
->PerfMonitor
.Groups
[i
];
81 m
->ActiveCounters
[i
] = rzalloc_array(m
->ActiveCounters
, BITSET_WORD
,
82 BITSET_WORDS(g
->NumCounters
));
83 if (m
->ActiveCounters
[i
] == NULL
)
90 ralloc_free(m
->ActiveGroups
);
91 ralloc_free(m
->ActiveCounters
);
92 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
97 free_performance_monitor(GLuint key
, void *data
, void *user
)
99 struct gl_perf_monitor_object
*m
= data
;
100 struct gl_context
*ctx
= user
;
102 ralloc_free(m
->ActiveGroups
);
103 ralloc_free(m
->ActiveCounters
);
104 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
108 _mesa_free_performance_monitors(struct gl_context
*ctx
)
110 _mesa_HashDeleteAll(ctx
->PerfMonitor
.Monitors
,
111 free_performance_monitor
, ctx
);
112 _mesa_DeleteHashTable(ctx
->PerfMonitor
.Monitors
);
115 static inline struct gl_perf_monitor_object
*
116 lookup_monitor(struct gl_context
*ctx
, GLuint id
)
118 return (struct gl_perf_monitor_object
*)
119 _mesa_HashLookup(ctx
->PerfMonitor
.Monitors
, id
);
122 static inline const struct gl_perf_monitor_group
*
123 get_group(const struct gl_context
*ctx
, GLuint id
)
125 if (id
>= ctx
->PerfMonitor
.NumGroups
)
128 return &ctx
->PerfMonitor
.Groups
[id
];
131 static inline const struct gl_perf_monitor_counter
*
132 get_counter(const struct gl_perf_monitor_group
*group_obj
, GLuint id
)
134 if (id
>= group_obj
->NumCounters
)
137 return &group_obj
->Counters
[id
];
140 /* For INTEL_performance_query, query id 0 is reserved to be invalid. We use
141 * index to Groups array + 1 as the query id. Same applies to counter id.
144 queryid_to_index(GLuint queryid
)
150 index_to_queryid(GLuint index
)
156 queryid_valid(const struct gl_context
*ctx
, GLuint queryid
)
158 return get_group(ctx
, queryid_to_index(queryid
)) != NULL
;
162 counterid_to_index(GLuint counterid
)
164 return counterid
- 1;
168 index_to_counterid(GLuint index
)
174 counterid_valid(const struct gl_perf_monitor_group
*group_obj
,
177 return get_counter(group_obj
, counterid_to_index(counterid
)) != NULL
;
180 /*****************************************************************************/
183 _mesa_GetPerfMonitorGroupsAMD(GLint
*numGroups
, GLsizei groupsSize
,
186 GET_CURRENT_CONTEXT(ctx
);
188 if (numGroups
!= NULL
)
189 *numGroups
= ctx
->PerfMonitor
.NumGroups
;
191 if (groupsSize
> 0 && groups
!= NULL
) {
193 unsigned n
= MIN2((GLuint
) groupsSize
, ctx
->PerfMonitor
.NumGroups
);
195 /* We just use the index in the Groups array as the ID. */
196 for (i
= 0; i
< n
; i
++)
202 _mesa_GetPerfMonitorCountersAMD(GLuint group
, GLint
*numCounters
,
203 GLint
*maxActiveCounters
,
204 GLsizei countersSize
, GLuint
*counters
)
206 GET_CURRENT_CONTEXT(ctx
);
207 const struct gl_perf_monitor_group
*group_obj
= get_group(ctx
, group
);
208 if (group_obj
== NULL
) {
209 _mesa_error(ctx
, GL_INVALID_VALUE
,
210 "glGetPerfMonitorCountersAMD(invalid group)");
214 if (maxActiveCounters
!= NULL
)
215 *maxActiveCounters
= group_obj
->MaxActiveCounters
;
217 if (numCounters
!= NULL
)
218 *numCounters
= group_obj
->NumCounters
;
220 if (counters
!= NULL
) {
222 unsigned n
= MIN2(group_obj
->NumCounters
, (GLuint
) countersSize
);
223 for (i
= 0; i
< n
; i
++) {
224 /* We just use the index in the Counters array as the ID. */
231 _mesa_GetPerfMonitorGroupStringAMD(GLuint group
, GLsizei bufSize
,
232 GLsizei
*length
, GLchar
*groupString
)
234 GET_CURRENT_CONTEXT(ctx
);
236 const struct gl_perf_monitor_group
*group_obj
= get_group(ctx
, group
);
238 if (group_obj
== NULL
) {
239 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGetPerfMonitorGroupStringAMD");
244 /* Return the number of characters that would be required to hold the
245 * group string, excluding the null terminator.
248 *length
= strlen(group_obj
->Name
);
251 *length
= MIN2(strlen(group_obj
->Name
), bufSize
);
252 if (groupString
!= NULL
)
253 strncpy(groupString
, group_obj
->Name
, bufSize
);
258 _mesa_GetPerfMonitorCounterStringAMD(GLuint group
, GLuint counter
,
259 GLsizei bufSize
, GLsizei
*length
,
260 GLchar
*counterString
)
262 GET_CURRENT_CONTEXT(ctx
);
264 const struct gl_perf_monitor_group
*group_obj
;
265 const struct gl_perf_monitor_counter
*counter_obj
;
267 group_obj
= get_group(ctx
, group
);
269 if (group_obj
== NULL
) {
270 _mesa_error(ctx
, GL_INVALID_VALUE
,
271 "glGetPerfMonitorCounterStringAMD(invalid group)");
275 counter_obj
= get_counter(group_obj
, counter
);
277 if (counter_obj
== NULL
) {
278 _mesa_error(ctx
, GL_INVALID_VALUE
,
279 "glGetPerfMonitorCounterStringAMD(invalid counter)");
284 /* Return the number of characters that would be required to hold the
285 * counter string, excluding the null terminator.
288 *length
= strlen(counter_obj
->Name
);
291 *length
= MIN2(strlen(counter_obj
->Name
), bufSize
);
292 if (counterString
!= NULL
)
293 strncpy(counterString
, counter_obj
->Name
, bufSize
);
298 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group
, GLuint counter
, GLenum pname
,
301 GET_CURRENT_CONTEXT(ctx
);
303 const struct gl_perf_monitor_group
*group_obj
;
304 const struct gl_perf_monitor_counter
*counter_obj
;
306 group_obj
= get_group(ctx
, group
);
308 if (group_obj
== NULL
) {
309 _mesa_error(ctx
, GL_INVALID_VALUE
,
310 "glGetPerfMonitorCounterInfoAMD(invalid group)");
314 counter_obj
= get_counter(group_obj
, counter
);
316 if (counter_obj
== NULL
) {
317 _mesa_error(ctx
, GL_INVALID_VALUE
,
318 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
323 case GL_COUNTER_TYPE_AMD
:
324 *((GLenum
*) data
) = counter_obj
->Type
;
327 case GL_COUNTER_RANGE_AMD
:
328 switch (counter_obj
->Type
) {
330 case GL_PERCENTAGE_AMD
: {
331 float *f_data
= data
;
332 f_data
[0] = counter_obj
->Minimum
.f
;
333 f_data
[1] = counter_obj
->Maximum
.f
;
336 case GL_UNSIGNED_INT
: {
337 uint32_t *u32_data
= data
;
338 u32_data
[0] = counter_obj
->Minimum
.u32
;
339 u32_data
[1] = counter_obj
->Maximum
.u32
;
342 case GL_UNSIGNED_INT64_AMD
: {
343 uint64_t *u64_data
= data
;
344 u64_data
[0] = counter_obj
->Minimum
.u64
;
345 u64_data
[1] = counter_obj
->Maximum
.u64
;
349 assert(!"Should not get here: invalid counter type");
354 _mesa_error(ctx
, GL_INVALID_ENUM
,
355 "glGetPerfMonitorCounterInfoAMD(pname)");
361 _mesa_GenPerfMonitorsAMD(GLsizei n
, GLuint
*monitors
)
364 GET_CURRENT_CONTEXT(ctx
);
366 if (MESA_VERBOSE
& VERBOSE_API
)
367 _mesa_debug(ctx
, "glGenPerfMonitorsAMD(%d)\n", n
);
370 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenPerfMonitorsAMD(n < 0)");
374 if (monitors
== NULL
)
377 /* We don't actually need them to be contiguous, but this is what
378 * the rest of Mesa does, so we may as well.
380 first
= _mesa_HashFindFreeKeyBlock(ctx
->PerfMonitor
.Monitors
, n
);
383 for (i
= 0; i
< n
; i
++) {
384 struct gl_perf_monitor_object
*m
=
385 new_performance_monitor(ctx
, first
+ i
);
387 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenPerfMonitorsAMD");
390 monitors
[i
] = first
+ i
;
391 _mesa_HashInsert(ctx
->PerfMonitor
.Monitors
, first
+ i
, m
);
394 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenPerfMonitorsAMD");
400 _mesa_DeletePerfMonitorsAMD(GLsizei n
, GLuint
*monitors
)
403 GET_CURRENT_CONTEXT(ctx
);
405 if (MESA_VERBOSE
& VERBOSE_API
)
406 _mesa_debug(ctx
, "glDeletePerfMonitorsAMD(%d)\n", n
);
409 _mesa_error(ctx
, GL_INVALID_VALUE
, "glDeletePerfMonitorsAMD(n < 0)");
413 if (monitors
== NULL
)
416 for (i
= 0; i
< n
; i
++) {
417 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitors
[i
]);
420 /* Give the driver a chance to stop the monitor if it's active. */
422 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
426 _mesa_HashRemove(ctx
->PerfMonitor
.Monitors
, monitors
[i
]);
427 ralloc_free(m
->ActiveGroups
);
428 ralloc_free(m
->ActiveCounters
);
429 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
431 /* "INVALID_VALUE error will be generated if any of the monitor IDs
432 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
433 * reference a valid generated monitor ID."
435 _mesa_error(ctx
, GL_INVALID_VALUE
,
436 "glDeletePerfMonitorsAMD(invalid monitor)");
442 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor
, GLboolean enable
,
443 GLuint group
, GLint numCounters
,
446 GET_CURRENT_CONTEXT(ctx
);
448 struct gl_perf_monitor_object
*m
;
449 const struct gl_perf_monitor_group
*group_obj
;
451 m
= lookup_monitor(ctx
, monitor
);
453 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
454 * SelectPerfMonitorCountersAMD does not reference a monitor created by
455 * GenPerfMonitorsAMD."
458 _mesa_error(ctx
, GL_INVALID_VALUE
,
459 "glSelectPerfMonitorCountersAMD(invalid monitor)");
463 group_obj
= get_group(ctx
, group
);
465 /* "INVALID_VALUE error will be generated if the <group> parameter to
466 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
467 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
468 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
470 if (group_obj
== NULL
) {
471 _mesa_error(ctx
, GL_INVALID_VALUE
,
472 "glSelectPerfMonitorCountersAMD(invalid group)");
476 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
477 * SelectPerfMonitorCountersAMD is less than 0."
479 if (numCounters
< 0) {
480 _mesa_error(ctx
, GL_INVALID_VALUE
,
481 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
485 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
486 * results for that monitor become invalidated and the result queries
487 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
489 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
491 /* Sanity check the counter ID list. */
492 for (i
= 0; i
< numCounters
; i
++) {
493 if (counterList
[i
] >= group_obj
->NumCounters
) {
494 _mesa_error(ctx
, GL_INVALID_VALUE
,
495 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
501 /* Enable the counters */
502 for (i
= 0; i
< numCounters
; i
++) {
503 ++m
->ActiveGroups
[group
];
504 BITSET_SET(m
->ActiveCounters
[group
], counterList
[i
]);
507 /* Disable the counters */
508 for (i
= 0; i
< numCounters
; i
++) {
509 --m
->ActiveGroups
[group
];
510 BITSET_CLEAR(m
->ActiveCounters
[group
], counterList
[i
]);
516 _mesa_BeginPerfMonitorAMD(GLuint monitor
)
518 GET_CURRENT_CONTEXT(ctx
);
520 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
523 _mesa_error(ctx
, GL_INVALID_VALUE
,
524 "glBeginPerfMonitorAMD(invalid monitor)");
528 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
529 * called when a performance monitor is already active."
532 _mesa_error(ctx
, GL_INVALID_OPERATION
,
533 "glBeginPerfMonitor(already active)");
537 /* The driver is free to return false if it can't begin monitoring for
538 * any reason. This translates into an INVALID_OPERATION error.
540 if (ctx
->Driver
.BeginPerfMonitor(ctx
, m
)) {
544 _mesa_error(ctx
, GL_INVALID_OPERATION
,
545 "glBeginPerfMonitor(driver unable to begin monitoring)");
550 _mesa_EndPerfMonitorAMD(GLuint monitor
)
552 GET_CURRENT_CONTEXT(ctx
);
554 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
557 _mesa_error(ctx
, GL_INVALID_VALUE
, "glEndPerfMonitorAMD(invalid monitor)");
561 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
562 * when a performance monitor is not currently started."
565 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glBeginPerfMonitor(not active)");
569 ctx
->Driver
.EndPerfMonitor(ctx
, m
);
576 * Return the number of bytes needed to store a monitor's result.
579 perf_monitor_result_size(const struct gl_context
*ctx
,
580 const struct gl_perf_monitor_object
*m
)
582 unsigned group
, counter
;
585 for (group
= 0; group
< ctx
->PerfMonitor
.NumGroups
; group
++) {
586 const struct gl_perf_monitor_group
*g
= &ctx
->PerfMonitor
.Groups
[group
];
587 for (counter
= 0; counter
< g
->NumCounters
; counter
++) {
588 const struct gl_perf_monitor_counter
*c
= &g
->Counters
[counter
];
590 if (!BITSET_TEST(m
->ActiveCounters
[group
], counter
))
593 size
+= sizeof(uint32_t); /* Group ID */
594 size
+= sizeof(uint32_t); /* Counter ID */
595 size
+= _mesa_perf_monitor_counter_size(c
);
602 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor
, GLenum pname
,
603 GLsizei dataSize
, GLuint
*data
,
606 GET_CURRENT_CONTEXT(ctx
);
608 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
609 bool result_available
;
612 _mesa_error(ctx
, GL_INVALID_VALUE
,
613 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
617 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
619 _mesa_error(ctx
, GL_INVALID_OPERATION
,
620 "glGetPerfMonitorCounterDataAMD(data == NULL)");
624 /* We need at least enough room for a single value. */
625 if (dataSize
< sizeof(GLuint
)) {
626 if (bytesWritten
!= NULL
)
631 /* If the monitor has never ended, there is no result. */
632 result_available
= m
->Ended
&&
633 ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
635 /* AMD appears to return 0 for all queries unless a result is available. */
636 if (!result_available
) {
638 if (bytesWritten
!= NULL
)
639 *bytesWritten
= sizeof(GLuint
);
644 case GL_PERFMON_RESULT_AVAILABLE_AMD
:
646 if (bytesWritten
!= NULL
)
647 *bytesWritten
= sizeof(GLuint
);
649 case GL_PERFMON_RESULT_SIZE_AMD
:
650 *data
= perf_monitor_result_size(ctx
, m
);
651 if (bytesWritten
!= NULL
)
652 *bytesWritten
= sizeof(GLuint
);
654 case GL_PERFMON_RESULT_AMD
:
655 ctx
->Driver
.GetPerfMonitorResult(ctx
, m
, dataSize
, data
, bytesWritten
);
658 _mesa_error(ctx
, GL_INVALID_ENUM
,
659 "glGetPerfMonitorCounterDataAMD(pname)");
664 * Returns how many bytes a counter's value takes up.
667 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter
*c
)
671 case GL_PERCENTAGE_AMD
:
672 return sizeof(GLfloat
);
673 case GL_UNSIGNED_INT
:
674 return sizeof(GLuint
);
675 case GL_UNSIGNED_INT64_AMD
:
676 return sizeof(uint64_t);
678 assert(!"Should not get here: invalid counter type");
683 extern void GLAPIENTRY
684 _mesa_GetFirstPerfQueryIdINTEL(GLuint
*queryId
)
686 GET_CURRENT_CONTEXT(ctx
);
689 /* The GL_INTEL_performance_query spec says:
691 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
694 _mesa_error(ctx
, GL_INVALID_VALUE
,
695 "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
699 numGroups
= ctx
->PerfMonitor
.NumGroups
;
701 /* The GL_INTEL_performance_query spec says:
703 * "If the given hardware platform doesn't support any performance
704 * queries, then the value of 0 is returned and INVALID_OPERATION error
707 if (numGroups
== 0) {
709 _mesa_error(ctx
, GL_INVALID_OPERATION
,
710 "glGetFirstPerfQueryIdINTEL(no queries supported)");
714 *queryId
= index_to_queryid(0);
717 extern void GLAPIENTRY
718 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId
, GLuint
*nextQueryId
)
720 GET_CURRENT_CONTEXT(ctx
);
722 /* The GL_INTEL_performance_query spec says:
724 * "The result is passed in location pointed by nextQueryId. If query
725 * identified by queryId is the last query available the value of 0 is
726 * returned. If the specified performance query identifier is invalid
727 * then INVALID_VALUE error is generated. If nextQueryId pointer is
728 * equal to 0, an INVALID_VALUE error is generated. Whenever error is
729 * generated, the value of 0 is returned."
733 _mesa_error(ctx
, GL_INVALID_VALUE
,
734 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
738 if (!queryid_valid(ctx
, queryId
)) {
740 _mesa_error(ctx
, GL_INVALID_VALUE
,
741 "glGetNextPerfQueryIdINTEL(invalid query)");
747 if (!queryid_valid(ctx
, queryId
)) {
750 *nextQueryId
= queryId
;
754 extern void GLAPIENTRY
755 _mesa_GetPerfQueryIdByNameINTEL(char *queryName
, GLuint
*queryId
)
757 GET_CURRENT_CONTEXT(ctx
);
760 /* The GL_INTEL_performance_query spec says:
762 * "If queryName does not reference a valid query name, an INVALID_VALUE
763 * error is generated."
766 _mesa_error(ctx
, GL_INVALID_VALUE
,
767 "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
771 /* The specification does not state that this produces an error. */
773 _mesa_warning(ctx
, "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
777 for (i
= 0; i
< ctx
->PerfMonitor
.NumGroups
; ++i
) {
778 const struct gl_perf_monitor_group
*group_obj
= get_group(ctx
, i
);
779 if (strcmp(group_obj
->Name
, queryName
) == 0) {
780 *queryId
= index_to_queryid(i
);
785 _mesa_error(ctx
, GL_INVALID_VALUE
,
786 "glGetPerfQueryIdByNameINTEL(invalid query name)");
789 extern void GLAPIENTRY
790 _mesa_GetPerfQueryInfoINTEL(GLuint queryId
,
791 GLuint queryNameLength
, char *queryName
,
792 GLuint
*dataSize
, GLuint
*noCounters
,
793 GLuint
*noActiveInstances
,
796 GET_CURRENT_CONTEXT(ctx
);
799 const struct gl_perf_monitor_group
*group_obj
=
800 get_group(ctx
, queryid_to_index(queryId
));
802 if (group_obj
== NULL
) {
803 /* The GL_INTEL_performance_query spec says:
805 * "If queryId does not reference a valid query type, an
806 * INVALID_VALUE error is generated."
808 _mesa_error(ctx
, GL_INVALID_VALUE
,
809 "glGetPerfQueryInfoINTEL(invalid query)");
814 strncpy(queryName
, group_obj
->Name
, queryNameLength
);
816 /* No specification given about whether the string needs to be
817 * zero-terminated. Zero-terminate the string always as we don't
818 * otherwise communicate the length of the returned string.
820 if (queryNameLength
> 0) {
821 queryName
[queryNameLength
- 1] = '\0';
827 for (i
= 0; i
< group_obj
->NumCounters
; ++i
) {
828 /* What we get from the driver is group id (uint32_t) + counter id
829 * (uint32_t) + value.
831 size
+= 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj
->Counters
[i
]);
837 *noCounters
= group_obj
->NumCounters
;
840 /* The GL_INTEL_performance_query spec says:
842 * "-- the actual number of already created query instances in
843 * maxInstances location"
845 * 1) Typo in the specification, should be noActiveInstances.
846 * 2) Another typo in the specification, maxInstances parameter is not listed
847 * in the declaration of this function in the list of new functions.
849 if (noActiveInstances
) {
850 *noActiveInstances
= _mesa_HashNumEntries(ctx
->PerfMonitor
.Monitors
);
854 /* TODO: This information not yet available in the monitor structs. For
855 * now, we hardcode SINGLE_CONTEXT, since that's what the implementation
856 * currently tries very hard to do.
858 *capsMask
= GL_PERFQUERY_SINGLE_CONTEXT_INTEL
;
862 extern void GLAPIENTRY
863 _mesa_GetPerfCounterInfoINTEL(GLuint queryId
, GLuint counterId
,
864 GLuint counterNameLength
, char *counterName
,
865 GLuint counterDescLength
, char *counterDesc
,
866 GLuint
*counterOffset
, GLuint
*counterDataSize
, GLuint
*counterTypeEnum
,
867 GLuint
*counterDataTypeEnum
, GLuint64
*rawCounterMaxValue
)
869 GET_CURRENT_CONTEXT(ctx
);
871 const struct gl_perf_monitor_group
*group_obj
;
872 const struct gl_perf_monitor_counter
*counter_obj
;
873 unsigned counterIndex
;
876 group_obj
= get_group(ctx
, queryid_to_index(queryId
));
878 /* The GL_INTEL_performance_query spec says:
880 * "If the pair of queryId and counterId does not reference a valid
881 * counter, an INVALID_VALUE error is generated."
883 if (group_obj
== NULL
) {
884 _mesa_error(ctx
, GL_INVALID_VALUE
,
885 "glGetPerfCounterInfoINTEL(invalid queryId)");
889 counterIndex
= counterid_to_index(counterId
);
890 counter_obj
= get_counter(group_obj
, counterIndex
);
892 if (counter_obj
== NULL
) {
893 _mesa_error(ctx
, GL_INVALID_VALUE
,
894 "glGetPerfCounterInfoINTEL(invalid counterId)");
899 strncpy(counterName
, counter_obj
->Name
, counterNameLength
);
901 /* No specification given about whether the string needs to be
902 * zero-terminated. Zero-terminate the string always as we don't
903 * otherwise communicate the length of the returned string.
905 if (counterNameLength
> 0) {
906 counterName
[counterNameLength
- 1] = '\0';
911 /* TODO: No separate description text at the moment. We pass the name
912 * again for the moment.
914 strncpy(counterDesc
, counter_obj
->Name
, counterDescLength
);
916 /* No specification given about whether the string needs to be
917 * zero-terminated. Zero-terminate the string always as we don't
918 * otherwise communicate the length of the returned string.
920 if (counterDescLength
> 0) {
921 counterDesc
[counterDescLength
- 1] = '\0';
927 for (i
= 0; i
< counterIndex
; ++i
) {
928 /* What we get from the driver is group id (uint32_t) + counter id
929 * (uint32_t) + value.
931 offset
+= 2 * sizeof(uint32_t) + _mesa_perf_monitor_counter_size(&group_obj
->Counters
[i
]);
933 *counterOffset
= 2 * sizeof(uint32_t) + offset
;
936 if (counterDataSize
) {
937 *counterDataSize
= _mesa_perf_monitor_counter_size(counter_obj
);
940 if (counterTypeEnum
) {
941 /* TODO: Different counter types (semantic type, not data type) not
942 * supported as of yet.
944 *counterTypeEnum
= GL_PERFQUERY_COUNTER_RAW_INTEL
;
947 if (counterDataTypeEnum
) {
948 switch (counter_obj
->Type
) {
950 case GL_PERCENTAGE_AMD
:
951 *counterDataTypeEnum
= GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL
;
953 case GL_UNSIGNED_INT
:
954 *counterDataTypeEnum
= GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL
;
956 case GL_UNSIGNED_INT64_AMD
:
957 *counterDataTypeEnum
= GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL
;
960 assert(!"Should not get here: invalid counter type");
965 if (rawCounterMaxValue
) {
966 /* This value is (implicitly) specified to be used only with
967 * GL_PERFQUERY_COUNTER_RAW_INTEL counters. When semantic types for
968 * counters are added, that needs to be checked.
971 /* The GL_INTEL_performance_query spec says:
973 * "for some raw counters for which the maximal value is
974 * deterministic, the maximal value of the counter in 1 second is
975 * returned in the location pointed by rawCounterMaxValue, otherwise,
976 * the location is written with the value of 0."
978 * The maximum value reported by the driver at the moment is not with
979 * these semantics, so write 0 always to be safe.
981 *rawCounterMaxValue
= 0;
985 extern void GLAPIENTRY
986 _mesa_CreatePerfQueryINTEL(GLuint queryId
, GLuint
*queryHandle
)
988 GET_CURRENT_CONTEXT(ctx
);
991 const struct gl_perf_monitor_group
*group_obj
;
992 struct gl_perf_monitor_object
*m
;
995 /* This is not specified in the extension, but is the only sane thing to
998 if (queryHandle
== NULL
) {
999 _mesa_error(ctx
, GL_INVALID_VALUE
,
1000 "glCreatePerfQueryINTEL(queryHandle == NULL)");
1004 group
= queryid_to_index(queryId
);
1005 group_obj
= get_group(ctx
, group
);
1007 /* The GL_INTEL_performance_query spec says:
1009 * "If queryId does not reference a valid query type, an INVALID_VALUE
1010 * error is generated."
1012 if (group_obj
== NULL
) {
1013 _mesa_error(ctx
, GL_INVALID_VALUE
,
1014 "glCreatePerfQueryINTEL(invalid queryId)");
1018 /* The query object created here is the counterpart of a `monitor' in
1019 * AMD_performance_monitor. This call is equivalent to calling
1020 * GenPerfMonitorsAMD and SelectPerfMonitorCountersAMD with a list of all
1021 * counters in a group.
1024 /* We keep the monitor ids contiguous */
1025 first
= _mesa_HashFindFreeKeyBlock(ctx
->PerfMonitor
.Monitors
, 1);
1027 /* The GL_INTEL_performance_query spec says:
1029 * "If the query instance cannot be created due to exceeding the
1030 * number of allowed instances or driver fails query creation due to
1031 * an insufficient memory reason, an OUT_OF_MEMORY error is
1032 * generated, and the location pointed by queryHandle returns NULL."
1034 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCreatePerfQueryINTEL");
1038 m
= new_performance_monitor(ctx
, first
);
1039 _mesa_HashInsert(ctx
->PerfMonitor
.Monitors
, first
, m
);
1040 *queryHandle
= first
;
1042 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
1044 for (i
= 0; i
< group_obj
->NumCounters
; ++i
) {
1045 ++m
->ActiveGroups
[group
];
1046 /* Counters are a continuous range of integers, 0 to NumCounters (excl),
1047 * so i is the counter value to use here.
1049 BITSET_SET(m
->ActiveCounters
[group
], i
);
1053 extern void GLAPIENTRY
1054 _mesa_DeletePerfQueryINTEL(GLuint queryHandle
)
1056 GET_CURRENT_CONTEXT(ctx
);
1057 struct gl_perf_monitor_object
*m
;
1059 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1062 m
= lookup_monitor(ctx
, queryHandle
);
1064 /* The GL_INTEL_performance_query spec says:
1066 * "If a query handle doesn't reference a previously created performance
1067 * query instance, an INVALID_VALUE error is generated."
1070 _mesa_error(ctx
, GL_INVALID_VALUE
,
1071 "glDeletePerfQueryINTEL(invalid queryHandle)");
1075 /* Let the driver stop the monitor if it's active. */
1077 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
1081 _mesa_HashRemove(ctx
->PerfMonitor
.Monitors
, queryHandle
);
1082 ralloc_free(m
->ActiveGroups
);
1083 ralloc_free(m
->ActiveCounters
);
1084 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
1087 extern void GLAPIENTRY
1088 _mesa_BeginPerfQueryINTEL(GLuint queryHandle
)
1090 GET_CURRENT_CONTEXT(ctx
);
1091 struct gl_perf_monitor_object
*m
;
1093 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1097 m
= lookup_monitor(ctx
, queryHandle
);
1099 /* The GL_INTEL_performance_query spec says:
1101 * "If a query handle doesn't reference a previously created performance
1102 * query instance, an INVALID_VALUE error is generated."
1105 _mesa_error(ctx
, GL_INVALID_VALUE
,
1106 "glBeginPerfQueryINTEL(invalid queryHandle)");
1110 /* The GL_INTEL_performance_query spec says:
1112 * "Note that some query types, they cannot be collected in the same
1113 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
1114 * they refer to queries of such different types. In such case
1115 * INVALID_OPERATION error is generated."
1117 * We also generate an INVALID_OPERATION error if the driver can't begin
1118 * monitoring for its own reasons, and for nesting the same query.
1121 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1122 "glBeginPerfQueryINTEL(already active)");
1126 if (ctx
->Driver
.BeginPerfMonitor(ctx
, m
)) {
1130 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1131 "glBeginPerfQueryINTEL(driver unable to begin monitoring)");
1135 extern void GLAPIENTRY
1136 _mesa_EndPerfQueryINTEL(GLuint queryHandle
)
1138 GET_CURRENT_CONTEXT(ctx
);
1139 struct gl_perf_monitor_object
*m
;
1141 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1145 m
= lookup_monitor(ctx
, queryHandle
);
1147 /* The GL_INTEL_performance_query spec says:
1149 * "If a performance query is not currently started, an
1150 * INVALID_OPERATION error will be generated."
1152 * The specification doesn't state that an invalid handle would be an
1153 * INVALID_VALUE error. Regardless, query for such a handle will not be
1154 * started, so we generate an INVALID_OPERATION in that case too.
1157 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1158 "glEndPerfQueryINTEL(invalid queryHandle)");
1163 _mesa_error(ctx
, GL_INVALID_OPERATION
,
1164 "glEndPerfQueryINTEL(not active)");
1168 ctx
->Driver
.EndPerfMonitor(ctx
, m
);
1174 extern void GLAPIENTRY
1175 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle
, GLuint flags
,
1176 GLsizei dataSize
, void *data
, GLuint
*bytesWritten
)
1178 GET_CURRENT_CONTEXT(ctx
);
1179 struct gl_perf_monitor_object
*m
;
1180 bool result_available
;
1182 /* The GL_INTEL_performance_query spec says:
1184 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE
1185 * error is generated."
1187 if (!bytesWritten
|| !data
) {
1188 _mesa_error(ctx
, GL_INVALID_VALUE
,
1189 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
1193 /* The queryHandle is the counterpart to AMD_performance_monitor's monitor
1197 m
= lookup_monitor(ctx
, queryHandle
);
1199 /* The specification doesn't state that an invalid handle generates an
1200 * error. We could interpret that to mean the case should be handled as
1201 * "measurement not ready for this query", but what should be done if
1202 * `flags' equals PERFQUERY_WAIT_INTEL?
1204 * To resolve this, we just generate an INVALID_VALUE from an invalid query
1208 _mesa_error(ctx
, GL_INVALID_VALUE
,
1209 "glGetPerfQueryDataINTEL(invalid queryHandle)");
1213 /* We need at least enough room for a single value. */
1214 if (dataSize
< sizeof(GLuint
)) {
1219 /* The GL_INTEL_performance_query spec says:
1221 * "The call may end without returning any data if they are not ready
1222 * for reading as the measurement session is still pending (the
1223 * EndPerfQueryINTEL() command processing is not finished by
1224 * hardware). In this case location pointed by the bytesWritten
1225 * parameter will be set to 0."
1227 * If EndPerfQueryINTEL() is not called at all, we follow this.
1234 result_available
= ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
1236 if (!result_available
) {
1237 if (flags
== GL_PERFQUERY_FLUSH_INTEL
) {
1238 ctx
->Driver
.Flush(ctx
);
1239 } else if (flags
== GL_PERFQUERY_WAIT_INTEL
) {
1240 /* Assume Finish() is both enough and not too much to wait for
1241 * results. If results are still not available after Finish(), the
1242 * later code automatically bails out with 0 for bytesWritten.
1244 ctx
->Driver
.Finish(ctx
);
1246 ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
1250 if (result_available
) {
1251 ctx
->Driver
.GetPerfMonitorResult(ctx
, m
, dataSize
, data
, (GLint
*)bytesWritten
);