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 /*****************************************************************************/
150 _mesa_GetPerfMonitorGroupsAMD(GLint
*numGroups
, GLsizei groupsSize
,
153 GET_CURRENT_CONTEXT(ctx
);
156 if (numGroups
!= NULL
)
157 *numGroups
= ctx
->PerfMonitor
.NumGroups
;
159 if (groupsSize
> 0 && groups
!= NULL
) {
161 unsigned n
= MIN2((GLuint
) groupsSize
, ctx
->PerfMonitor
.NumGroups
);
163 /* We just use the index in the Groups array as the ID. */
164 for (i
= 0; i
< n
; i
++)
170 _mesa_GetPerfMonitorCountersAMD(GLuint group
, GLint
*numCounters
,
171 GLint
*maxActiveCounters
,
172 GLsizei countersSize
, GLuint
*counters
)
174 GET_CURRENT_CONTEXT(ctx
);
175 const struct gl_perf_monitor_group
*group_obj
;
179 group_obj
= get_group(ctx
, group
);
180 if (group_obj
== NULL
) {
181 _mesa_error(ctx
, GL_INVALID_VALUE
,
182 "glGetPerfMonitorCountersAMD(invalid group)");
186 if (maxActiveCounters
!= NULL
)
187 *maxActiveCounters
= group_obj
->MaxActiveCounters
;
189 if (numCounters
!= NULL
)
190 *numCounters
= group_obj
->NumCounters
;
192 if (counters
!= NULL
) {
194 unsigned n
= MIN2(group_obj
->NumCounters
, (GLuint
) countersSize
);
195 for (i
= 0; i
< n
; i
++) {
196 /* We just use the index in the Counters array as the ID. */
203 _mesa_GetPerfMonitorGroupStringAMD(GLuint group
, GLsizei bufSize
,
204 GLsizei
*length
, GLchar
*groupString
)
206 GET_CURRENT_CONTEXT(ctx
);
207 const struct gl_perf_monitor_group
*group_obj
;
211 group_obj
= get_group(ctx
, group
);
212 if (group_obj
== NULL
) {
213 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGetPerfMonitorGroupStringAMD");
218 /* Return the number of characters that would be required to hold the
219 * group string, excluding the null terminator.
222 *length
= strlen(group_obj
->Name
);
225 *length
= MIN2(strlen(group_obj
->Name
), bufSize
);
226 if (groupString
!= NULL
)
227 strncpy(groupString
, group_obj
->Name
, bufSize
);
232 _mesa_GetPerfMonitorCounterStringAMD(GLuint group
, GLuint counter
,
233 GLsizei bufSize
, GLsizei
*length
,
234 GLchar
*counterString
)
236 GET_CURRENT_CONTEXT(ctx
);
238 const struct gl_perf_monitor_group
*group_obj
;
239 const struct gl_perf_monitor_counter
*counter_obj
;
243 group_obj
= get_group(ctx
, group
);
245 if (group_obj
== NULL
) {
246 _mesa_error(ctx
, GL_INVALID_VALUE
,
247 "glGetPerfMonitorCounterStringAMD(invalid group)");
251 counter_obj
= get_counter(group_obj
, counter
);
253 if (counter_obj
== NULL
) {
254 _mesa_error(ctx
, GL_INVALID_VALUE
,
255 "glGetPerfMonitorCounterStringAMD(invalid counter)");
260 /* Return the number of characters that would be required to hold the
261 * counter string, excluding the null terminator.
264 *length
= strlen(counter_obj
->Name
);
267 *length
= MIN2(strlen(counter_obj
->Name
), bufSize
);
268 if (counterString
!= NULL
)
269 strncpy(counterString
, counter_obj
->Name
, bufSize
);
274 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group
, GLuint counter
, GLenum pname
,
277 GET_CURRENT_CONTEXT(ctx
);
279 const struct gl_perf_monitor_group
*group_obj
;
280 const struct gl_perf_monitor_counter
*counter_obj
;
284 group_obj
= get_group(ctx
, group
);
286 if (group_obj
== NULL
) {
287 _mesa_error(ctx
, GL_INVALID_VALUE
,
288 "glGetPerfMonitorCounterInfoAMD(invalid group)");
292 counter_obj
= get_counter(group_obj
, counter
);
294 if (counter_obj
== NULL
) {
295 _mesa_error(ctx
, GL_INVALID_VALUE
,
296 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
301 case GL_COUNTER_TYPE_AMD
:
302 *((GLenum
*) data
) = counter_obj
->Type
;
305 case GL_COUNTER_RANGE_AMD
:
306 switch (counter_obj
->Type
) {
308 case GL_PERCENTAGE_AMD
: {
309 float *f_data
= data
;
310 f_data
[0] = counter_obj
->Minimum
.f
;
311 f_data
[1] = counter_obj
->Maximum
.f
;
314 case GL_UNSIGNED_INT
: {
315 uint32_t *u32_data
= data
;
316 u32_data
[0] = counter_obj
->Minimum
.u32
;
317 u32_data
[1] = counter_obj
->Maximum
.u32
;
320 case GL_UNSIGNED_INT64_AMD
: {
321 uint64_t *u64_data
= data
;
322 u64_data
[0] = counter_obj
->Minimum
.u64
;
323 u64_data
[1] = counter_obj
->Maximum
.u64
;
327 assert(!"Should not get here: invalid counter type");
332 _mesa_error(ctx
, GL_INVALID_ENUM
,
333 "glGetPerfMonitorCounterInfoAMD(pname)");
339 _mesa_GenPerfMonitorsAMD(GLsizei n
, GLuint
*monitors
)
342 GET_CURRENT_CONTEXT(ctx
);
344 if (MESA_VERBOSE
& VERBOSE_API
)
345 _mesa_debug(ctx
, "glGenPerfMonitorsAMD(%d)\n", n
);
350 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenPerfMonitorsAMD(n < 0)");
354 if (monitors
== NULL
)
357 /* We don't actually need them to be contiguous, but this is what
358 * the rest of Mesa does, so we may as well.
360 first
= _mesa_HashFindFreeKeyBlock(ctx
->PerfMonitor
.Monitors
, n
);
363 for (i
= 0; i
< n
; i
++) {
364 struct gl_perf_monitor_object
*m
=
365 new_performance_monitor(ctx
, first
+ i
);
367 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenPerfMonitorsAMD");
370 monitors
[i
] = first
+ i
;
371 _mesa_HashInsert(ctx
->PerfMonitor
.Monitors
, first
+ i
, m
);
374 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glGenPerfMonitorsAMD");
380 _mesa_DeletePerfMonitorsAMD(GLsizei n
, GLuint
*monitors
)
383 GET_CURRENT_CONTEXT(ctx
);
385 if (MESA_VERBOSE
& VERBOSE_API
)
386 _mesa_debug(ctx
, "glDeletePerfMonitorsAMD(%d)\n", n
);
389 _mesa_error(ctx
, GL_INVALID_VALUE
, "glDeletePerfMonitorsAMD(n < 0)");
393 if (monitors
== NULL
)
396 for (i
= 0; i
< n
; i
++) {
397 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitors
[i
]);
400 /* Give the driver a chance to stop the monitor if it's active. */
402 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
406 _mesa_HashRemove(ctx
->PerfMonitor
.Monitors
, monitors
[i
]);
407 ralloc_free(m
->ActiveGroups
);
408 ralloc_free(m
->ActiveCounters
);
409 ctx
->Driver
.DeletePerfMonitor(ctx
, m
);
411 /* "INVALID_VALUE error will be generated if any of the monitor IDs
412 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
413 * reference a valid generated monitor ID."
415 _mesa_error(ctx
, GL_INVALID_VALUE
,
416 "glDeletePerfMonitorsAMD(invalid monitor)");
422 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor
, GLboolean enable
,
423 GLuint group
, GLint numCounters
,
426 GET_CURRENT_CONTEXT(ctx
);
428 struct gl_perf_monitor_object
*m
;
429 const struct gl_perf_monitor_group
*group_obj
;
431 m
= lookup_monitor(ctx
, monitor
);
433 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
434 * SelectPerfMonitorCountersAMD does not reference a monitor created by
435 * GenPerfMonitorsAMD."
438 _mesa_error(ctx
, GL_INVALID_VALUE
,
439 "glSelectPerfMonitorCountersAMD(invalid monitor)");
443 group_obj
= get_group(ctx
, group
);
445 /* "INVALID_VALUE error will be generated if the <group> parameter to
446 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
447 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
448 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
450 if (group_obj
== NULL
) {
451 _mesa_error(ctx
, GL_INVALID_VALUE
,
452 "glSelectPerfMonitorCountersAMD(invalid group)");
456 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
457 * SelectPerfMonitorCountersAMD is less than 0."
459 if (numCounters
< 0) {
460 _mesa_error(ctx
, GL_INVALID_VALUE
,
461 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
465 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
466 * results for that monitor become invalidated and the result queries
467 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
469 ctx
->Driver
.ResetPerfMonitor(ctx
, m
);
471 /* Sanity check the counter ID list. */
472 for (i
= 0; i
< numCounters
; i
++) {
473 if (counterList
[i
] >= group_obj
->NumCounters
) {
474 _mesa_error(ctx
, GL_INVALID_VALUE
,
475 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
481 /* Enable the counters */
482 for (i
= 0; i
< numCounters
; i
++) {
483 ++m
->ActiveGroups
[group
];
484 BITSET_SET(m
->ActiveCounters
[group
], counterList
[i
]);
487 /* Disable the counters */
488 for (i
= 0; i
< numCounters
; i
++) {
489 --m
->ActiveGroups
[group
];
490 BITSET_CLEAR(m
->ActiveCounters
[group
], counterList
[i
]);
496 _mesa_BeginPerfMonitorAMD(GLuint monitor
)
498 GET_CURRENT_CONTEXT(ctx
);
500 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
503 _mesa_error(ctx
, GL_INVALID_VALUE
,
504 "glBeginPerfMonitorAMD(invalid monitor)");
508 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
509 * called when a performance monitor is already active."
512 _mesa_error(ctx
, GL_INVALID_OPERATION
,
513 "glBeginPerfMonitor(already active)");
517 /* The driver is free to return false if it can't begin monitoring for
518 * any reason. This translates into an INVALID_OPERATION error.
520 if (ctx
->Driver
.BeginPerfMonitor(ctx
, m
)) {
524 _mesa_error(ctx
, GL_INVALID_OPERATION
,
525 "glBeginPerfMonitor(driver unable to begin monitoring)");
530 _mesa_EndPerfMonitorAMD(GLuint monitor
)
532 GET_CURRENT_CONTEXT(ctx
);
534 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
537 _mesa_error(ctx
, GL_INVALID_VALUE
, "glEndPerfMonitorAMD(invalid monitor)");
541 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
542 * when a performance monitor is not currently started."
545 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glBeginPerfMonitor(not active)");
549 ctx
->Driver
.EndPerfMonitor(ctx
, m
);
556 * Return the number of bytes needed to store a monitor's result.
559 perf_monitor_result_size(const struct gl_context
*ctx
,
560 const struct gl_perf_monitor_object
*m
)
562 unsigned group
, counter
;
565 for (group
= 0; group
< ctx
->PerfMonitor
.NumGroups
; group
++) {
566 const struct gl_perf_monitor_group
*g
= &ctx
->PerfMonitor
.Groups
[group
];
569 BITSET_FOREACH_SET(counter
, tmp
, m
->ActiveCounters
[group
], g
->NumCounters
) {
570 const struct gl_perf_monitor_counter
*c
= &g
->Counters
[counter
];
572 size
+= sizeof(uint32_t); /* Group ID */
573 size
+= sizeof(uint32_t); /* Counter ID */
574 size
+= _mesa_perf_monitor_counter_size(c
);
581 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor
, GLenum pname
,
582 GLsizei dataSize
, GLuint
*data
,
585 GET_CURRENT_CONTEXT(ctx
);
587 struct gl_perf_monitor_object
*m
= lookup_monitor(ctx
, monitor
);
588 bool result_available
;
591 _mesa_error(ctx
, GL_INVALID_VALUE
,
592 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
596 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
598 _mesa_error(ctx
, GL_INVALID_OPERATION
,
599 "glGetPerfMonitorCounterDataAMD(data == NULL)");
603 /* We need at least enough room for a single value. */
604 if (dataSize
< sizeof(GLuint
)) {
605 if (bytesWritten
!= NULL
)
610 /* If the monitor has never ended, there is no result. */
611 result_available
= m
->Ended
&&
612 ctx
->Driver
.IsPerfMonitorResultAvailable(ctx
, m
);
614 /* AMD appears to return 0 for all queries unless a result is available. */
615 if (!result_available
) {
617 if (bytesWritten
!= NULL
)
618 *bytesWritten
= sizeof(GLuint
);
623 case GL_PERFMON_RESULT_AVAILABLE_AMD
:
625 if (bytesWritten
!= NULL
)
626 *bytesWritten
= sizeof(GLuint
);
628 case GL_PERFMON_RESULT_SIZE_AMD
:
629 *data
= perf_monitor_result_size(ctx
, m
);
630 if (bytesWritten
!= NULL
)
631 *bytesWritten
= sizeof(GLuint
);
633 case GL_PERFMON_RESULT_AMD
:
634 ctx
->Driver
.GetPerfMonitorResult(ctx
, m
, dataSize
, data
, bytesWritten
);
637 _mesa_error(ctx
, GL_INVALID_ENUM
,
638 "glGetPerfMonitorCounterDataAMD(pname)");
643 * Returns how many bytes a counter's value takes up.
646 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter
*c
)
650 case GL_PERCENTAGE_AMD
:
651 return sizeof(GLfloat
);
652 case GL_UNSIGNED_INT
:
653 return sizeof(GLuint
);
654 case GL_UNSIGNED_INT64_AMD
:
655 return sizeof(uint64_t);
657 assert(!"Should not get here: invalid counter type");