mesa: Validate image units when the texture state changes.
[mesa.git] / src / mesa / main / performance_monitor.c
1 /*
2 * Copyright © 2012 Intel Corporation
3 *
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:
10 *
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
13 * Software.
14 *
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.
22 */
23
24 /**
25 * \file performance_monitor.c
26 * Core Mesa support for the AMD_performance_monitor extension.
27 *
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.
31 *
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.
35 */
36
37 #include <stdbool.h>
38 #include "glheader.h"
39 #include "context.h"
40 #include "enums.h"
41 #include "hash.h"
42 #include "macros.h"
43 #include "mtypes.h"
44 #include "performance_monitor.h"
45 #include "bitset.h"
46 #include "ralloc.h"
47
48 void
49 _mesa_init_performance_monitors(struct gl_context *ctx)
50 {
51 ctx->PerfMonitor.Monitors = _mesa_NewHashTable();
52 ctx->PerfMonitor.NumGroups = 0;
53 ctx->PerfMonitor.Groups = NULL;
54 }
55
56 static struct gl_perf_monitor_object *
57 new_performance_monitor(struct gl_context *ctx, GLuint index)
58 {
59 unsigned i;
60 struct gl_perf_monitor_object *m = ctx->Driver.NewPerfMonitor(ctx);
61
62 if (m == NULL)
63 return NULL;
64
65 m->Name = index;
66
67 m->Active = false;
68
69 m->ActiveGroups =
70 rzalloc_array(NULL, unsigned, ctx->PerfMonitor.NumGroups);
71
72 m->ActiveCounters =
73 ralloc_array(NULL, BITSET_WORD *, ctx->PerfMonitor.NumGroups);
74
75 if (m->ActiveGroups == NULL || m->ActiveCounters == NULL)
76 goto fail;
77
78 for (i = 0; i < ctx->PerfMonitor.NumGroups; i++) {
79 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[i];
80
81 m->ActiveCounters[i] = rzalloc_array(m->ActiveCounters, BITSET_WORD,
82 BITSET_WORDS(g->NumCounters));
83 if (m->ActiveCounters[i] == NULL)
84 goto fail;
85 }
86
87 return m;
88
89 fail:
90 ralloc_free(m->ActiveGroups);
91 ralloc_free(m->ActiveCounters);
92 ctx->Driver.DeletePerfMonitor(ctx, m);
93 return NULL;
94 }
95
96 static void
97 free_performance_monitor(GLuint key, void *data, void *user)
98 {
99 struct gl_perf_monitor_object *m = data;
100 struct gl_context *ctx = user;
101
102 ralloc_free(m->ActiveGroups);
103 ralloc_free(m->ActiveCounters);
104 ctx->Driver.DeletePerfMonitor(ctx, m);
105 }
106
107 void
108 _mesa_free_performance_monitors(struct gl_context *ctx)
109 {
110 _mesa_HashDeleteAll(ctx->PerfMonitor.Monitors,
111 free_performance_monitor, ctx);
112 _mesa_DeleteHashTable(ctx->PerfMonitor.Monitors);
113 }
114
115 static inline struct gl_perf_monitor_object *
116 lookup_monitor(struct gl_context *ctx, GLuint id)
117 {
118 return (struct gl_perf_monitor_object *)
119 _mesa_HashLookup(ctx->PerfMonitor.Monitors, id);
120 }
121
122 static inline const struct gl_perf_monitor_group *
123 get_group(const struct gl_context *ctx, GLuint id)
124 {
125 if (id >= ctx->PerfMonitor.NumGroups)
126 return NULL;
127
128 return &ctx->PerfMonitor.Groups[id];
129 }
130
131 static inline const struct gl_perf_monitor_counter *
132 get_counter(const struct gl_perf_monitor_group *group_obj, GLuint id)
133 {
134 if (id >= group_obj->NumCounters)
135 return NULL;
136
137 return &group_obj->Counters[id];
138 }
139
140 /*****************************************************************************/
141
142 void GLAPIENTRY
143 _mesa_GetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize,
144 GLuint *groups)
145 {
146 GET_CURRENT_CONTEXT(ctx);
147
148 if (numGroups != NULL)
149 *numGroups = ctx->PerfMonitor.NumGroups;
150
151 if (groupsSize > 0 && groups != NULL) {
152 unsigned i;
153 unsigned n = MIN2((GLuint) groupsSize, ctx->PerfMonitor.NumGroups);
154
155 /* We just use the index in the Groups array as the ID. */
156 for (i = 0; i < n; i++)
157 groups[i] = i;
158 }
159 }
160
161 void GLAPIENTRY
162 _mesa_GetPerfMonitorCountersAMD(GLuint group, GLint *numCounters,
163 GLint *maxActiveCounters,
164 GLsizei countersSize, GLuint *counters)
165 {
166 GET_CURRENT_CONTEXT(ctx);
167 const struct gl_perf_monitor_group *group_obj = get_group(ctx, group);
168 if (group_obj == NULL) {
169 _mesa_error(ctx, GL_INVALID_VALUE,
170 "glGetPerfMonitorCountersAMD(invalid group)");
171 return;
172 }
173
174 if (maxActiveCounters != NULL)
175 *maxActiveCounters = group_obj->MaxActiveCounters;
176
177 if (numCounters != NULL)
178 *numCounters = group_obj->NumCounters;
179
180 if (counters != NULL) {
181 unsigned i;
182 unsigned n = MIN2(group_obj->NumCounters, (GLuint) countersSize);
183 for (i = 0; i < n; i++) {
184 /* We just use the index in the Counters array as the ID. */
185 counters[i] = i;
186 }
187 }
188 }
189
190 void GLAPIENTRY
191 _mesa_GetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize,
192 GLsizei *length, GLchar *groupString)
193 {
194 GET_CURRENT_CONTEXT(ctx);
195
196 const struct gl_perf_monitor_group *group_obj = get_group(ctx, group);
197
198 if (group_obj == NULL) {
199 _mesa_error(ctx, GL_INVALID_VALUE, "glGetPerfMonitorGroupStringAMD");
200 return;
201 }
202
203 if (bufSize == 0) {
204 /* Return the number of characters that would be required to hold the
205 * group string, excluding the null terminator.
206 */
207 if (length != NULL)
208 *length = strlen(group_obj->Name);
209 } else {
210 if (length != NULL)
211 *length = MIN2(strlen(group_obj->Name), bufSize);
212 if (groupString != NULL)
213 strncpy(groupString, group_obj->Name, bufSize);
214 }
215 }
216
217 void GLAPIENTRY
218 _mesa_GetPerfMonitorCounterStringAMD(GLuint group, GLuint counter,
219 GLsizei bufSize, GLsizei *length,
220 GLchar *counterString)
221 {
222 GET_CURRENT_CONTEXT(ctx);
223
224 const struct gl_perf_monitor_group *group_obj;
225 const struct gl_perf_monitor_counter *counter_obj;
226
227 group_obj = get_group(ctx, group);
228
229 if (group_obj == NULL) {
230 _mesa_error(ctx, GL_INVALID_VALUE,
231 "glGetPerfMonitorCounterStringAMD(invalid group)");
232 return;
233 }
234
235 counter_obj = get_counter(group_obj, counter);
236
237 if (counter_obj == NULL) {
238 _mesa_error(ctx, GL_INVALID_VALUE,
239 "glGetPerfMonitorCounterStringAMD(invalid counter)");
240 return;
241 }
242
243 if (bufSize == 0) {
244 /* Return the number of characters that would be required to hold the
245 * counter string, excluding the null terminator.
246 */
247 if (length != NULL)
248 *length = strlen(counter_obj->Name);
249 } else {
250 if (length != NULL)
251 *length = MIN2(strlen(counter_obj->Name), bufSize);
252 if (counterString != NULL)
253 strncpy(counterString, counter_obj->Name, bufSize);
254 }
255 }
256
257 void GLAPIENTRY
258 _mesa_GetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum pname,
259 GLvoid *data)
260 {
261 GET_CURRENT_CONTEXT(ctx);
262
263 const struct gl_perf_monitor_group *group_obj;
264 const struct gl_perf_monitor_counter *counter_obj;
265
266 group_obj = get_group(ctx, group);
267
268 if (group_obj == NULL) {
269 _mesa_error(ctx, GL_INVALID_VALUE,
270 "glGetPerfMonitorCounterInfoAMD(invalid group)");
271 return;
272 }
273
274 counter_obj = get_counter(group_obj, counter);
275
276 if (counter_obj == NULL) {
277 _mesa_error(ctx, GL_INVALID_VALUE,
278 "glGetPerfMonitorCounterInfoAMD(invalid counter)");
279 return;
280 }
281
282 switch (pname) {
283 case GL_COUNTER_TYPE_AMD:
284 *((GLenum *) data) = counter_obj->Type;
285 break;
286
287 case GL_COUNTER_RANGE_AMD:
288 switch (counter_obj->Type) {
289 case GL_FLOAT:
290 case GL_PERCENTAGE_AMD: {
291 float *f_data = data;
292 f_data[0] = counter_obj->Minimum.f;
293 f_data[1] = counter_obj->Maximum.f;
294 break;
295 }
296 case GL_UNSIGNED_INT: {
297 uint32_t *u32_data = data;
298 u32_data[0] = counter_obj->Minimum.u32;
299 u32_data[1] = counter_obj->Maximum.u32;
300 break;
301 }
302 case GL_UNSIGNED_INT64_AMD: {
303 uint64_t *u64_data = data;
304 u64_data[0] = counter_obj->Minimum.u64;
305 u64_data[1] = counter_obj->Maximum.u64;
306 break;
307 }
308 default:
309 assert(!"Should not get here: invalid counter type");
310 }
311 break;
312
313 default:
314 _mesa_error(ctx, GL_INVALID_ENUM,
315 "glGetPerfMonitorCounterInfoAMD(pname)");
316 return;
317 }
318 }
319
320 void GLAPIENTRY
321 _mesa_GenPerfMonitorsAMD(GLsizei n, GLuint *monitors)
322 {
323 GLuint first;
324 GET_CURRENT_CONTEXT(ctx);
325
326 if (MESA_VERBOSE & VERBOSE_API)
327 _mesa_debug(ctx, "glGenPerfMonitorsAMD(%d)\n", n);
328
329 if (n < 0) {
330 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPerfMonitorsAMD(n < 0)");
331 return;
332 }
333
334 if (monitors == NULL)
335 return;
336
337 /* We don't actually need them to be contiguous, but this is what
338 * the rest of Mesa does, so we may as well.
339 */
340 first = _mesa_HashFindFreeKeyBlock(ctx->PerfMonitor.Monitors, n);
341 if (first) {
342 GLsizei i;
343 for (i = 0; i < n; i++) {
344 struct gl_perf_monitor_object *m =
345 new_performance_monitor(ctx, first + i);
346 if (!m) {
347 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
348 return;
349 }
350 monitors[i] = first + i;
351 _mesa_HashInsert(ctx->PerfMonitor.Monitors, first + i, m);
352 }
353 } else {
354 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenPerfMonitorsAMD");
355 return;
356 }
357 }
358
359 void GLAPIENTRY
360 _mesa_DeletePerfMonitorsAMD(GLsizei n, GLuint *monitors)
361 {
362 GLint i;
363 GET_CURRENT_CONTEXT(ctx);
364
365 if (MESA_VERBOSE & VERBOSE_API)
366 _mesa_debug(ctx, "glDeletePerfMonitorsAMD(%d)\n", n);
367
368 if (n < 0) {
369 _mesa_error(ctx, GL_INVALID_VALUE, "glDeletePerfMonitorsAMD(n < 0)");
370 return;
371 }
372
373 if (monitors == NULL)
374 return;
375
376 for (i = 0; i < n; i++) {
377 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitors[i]);
378
379 if (m) {
380 /* Give the driver a chance to stop the monitor if it's active. */
381 if (m->Active) {
382 ctx->Driver.ResetPerfMonitor(ctx, m);
383 m->Ended = false;
384 }
385
386 _mesa_HashRemove(ctx->PerfMonitor.Monitors, monitors[i]);
387 ralloc_free(m->ActiveGroups);
388 ralloc_free(m->ActiveCounters);
389 ctx->Driver.DeletePerfMonitor(ctx, m);
390 } else {
391 /* "INVALID_VALUE error will be generated if any of the monitor IDs
392 * in the <monitors> parameter to DeletePerfMonitorsAMD do not
393 * reference a valid generated monitor ID."
394 */
395 _mesa_error(ctx, GL_INVALID_VALUE,
396 "glDeletePerfMonitorsAMD(invalid monitor)");
397 }
398 }
399 }
400
401 void GLAPIENTRY
402 _mesa_SelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable,
403 GLuint group, GLint numCounters,
404 GLuint *counterList)
405 {
406 GET_CURRENT_CONTEXT(ctx);
407 int i;
408 struct gl_perf_monitor_object *m;
409 const struct gl_perf_monitor_group *group_obj;
410
411 m = lookup_monitor(ctx, monitor);
412
413 /* "INVALID_VALUE error will be generated if the <monitor> parameter to
414 * SelectPerfMonitorCountersAMD does not reference a monitor created by
415 * GenPerfMonitorsAMD."
416 */
417 if (m == NULL) {
418 _mesa_error(ctx, GL_INVALID_VALUE,
419 "glSelectPerfMonitorCountersAMD(invalid monitor)");
420 return;
421 }
422
423 group_obj = get_group(ctx, group);
424
425 /* "INVALID_VALUE error will be generated if the <group> parameter to
426 * GetPerfMonitorCountersAMD, GetPerfMonitorCounterStringAMD,
427 * GetPerfMonitorCounterStringAMD, GetPerfMonitorCounterInfoAMD, or
428 * SelectPerfMonitorCountersAMD does not reference a valid group ID."
429 */
430 if (group_obj == NULL) {
431 _mesa_error(ctx, GL_INVALID_VALUE,
432 "glSelectPerfMonitorCountersAMD(invalid group)");
433 return;
434 }
435
436 /* "INVALID_VALUE error will be generated if the <numCounters> parameter to
437 * SelectPerfMonitorCountersAMD is less than 0."
438 */
439 if (numCounters < 0) {
440 _mesa_error(ctx, GL_INVALID_VALUE,
441 "glSelectPerfMonitorCountersAMD(numCounters < 0)");
442 return;
443 }
444
445 /* "When SelectPerfMonitorCountersAMD is called on a monitor, any outstanding
446 * results for that monitor become invalidated and the result queries
447 * PERFMON_RESULT_SIZE_AMD and PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
448 */
449 ctx->Driver.ResetPerfMonitor(ctx, m);
450
451 /* Sanity check the counter ID list. */
452 for (i = 0; i < numCounters; i++) {
453 if (counterList[i] >= group_obj->NumCounters) {
454 _mesa_error(ctx, GL_INVALID_VALUE,
455 "glSelectPerfMonitorCountersAMD(invalid counter ID)");
456 return;
457 }
458 }
459
460 if (enable) {
461 /* Enable the counters */
462 for (i = 0; i < numCounters; i++) {
463 ++m->ActiveGroups[group];
464 BITSET_SET(m->ActiveCounters[group], counterList[i]);
465 }
466 } else {
467 /* Disable the counters */
468 for (i = 0; i < numCounters; i++) {
469 --m->ActiveGroups[group];
470 BITSET_CLEAR(m->ActiveCounters[group], counterList[i]);
471 }
472 }
473 }
474
475 void GLAPIENTRY
476 _mesa_BeginPerfMonitorAMD(GLuint monitor)
477 {
478 GET_CURRENT_CONTEXT(ctx);
479
480 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
481
482 if (m == NULL) {
483 _mesa_error(ctx, GL_INVALID_VALUE,
484 "glBeginPerfMonitorAMD(invalid monitor)");
485 return;
486 }
487
488 /* "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
489 * called when a performance monitor is already active."
490 */
491 if (m->Active) {
492 _mesa_error(ctx, GL_INVALID_OPERATION,
493 "glBeginPerfMonitor(already active)");
494 return;
495 }
496
497 /* The driver is free to return false if it can't begin monitoring for
498 * any reason. This translates into an INVALID_OPERATION error.
499 */
500 if (ctx->Driver.BeginPerfMonitor(ctx, m)) {
501 m->Active = true;
502 m->Ended = false;
503 } else {
504 _mesa_error(ctx, GL_INVALID_OPERATION,
505 "glBeginPerfMonitor(driver unable to begin monitoring)");
506 }
507 }
508
509 void GLAPIENTRY
510 _mesa_EndPerfMonitorAMD(GLuint monitor)
511 {
512 GET_CURRENT_CONTEXT(ctx);
513
514 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
515
516 if (m == NULL) {
517 _mesa_error(ctx, GL_INVALID_VALUE, "glEndPerfMonitorAMD(invalid monitor)");
518 return;
519 }
520
521 /* "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is called
522 * when a performance monitor is not currently started."
523 */
524 if (!m->Active) {
525 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginPerfMonitor(not active)");
526 return;
527 }
528
529 ctx->Driver.EndPerfMonitor(ctx, m);
530
531 m->Active = false;
532 m->Ended = true;
533 }
534
535 /**
536 * Return the number of bytes needed to store a monitor's result.
537 */
538 static unsigned
539 perf_monitor_result_size(const struct gl_context *ctx,
540 const struct gl_perf_monitor_object *m)
541 {
542 unsigned group, counter;
543 unsigned size = 0;
544
545 for (group = 0; group < ctx->PerfMonitor.NumGroups; group++) {
546 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[group];
547 for (counter = 0; counter < g->NumCounters; counter++) {
548 const struct gl_perf_monitor_counter *c = &g->Counters[counter];
549
550 if (!BITSET_TEST(m->ActiveCounters[group], counter))
551 continue;
552
553 size += sizeof(uint32_t); /* Group ID */
554 size += sizeof(uint32_t); /* Counter ID */
555 size += _mesa_perf_monitor_counter_size(c);
556 }
557 }
558 return size;
559 }
560
561 void GLAPIENTRY
562 _mesa_GetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname,
563 GLsizei dataSize, GLuint *data,
564 GLint *bytesWritten)
565 {
566 GET_CURRENT_CONTEXT(ctx);
567
568 struct gl_perf_monitor_object *m = lookup_monitor(ctx, monitor);
569 bool result_available;
570
571 if (m == NULL) {
572 _mesa_error(ctx, GL_INVALID_VALUE,
573 "glGetPerfMonitorCounterDataAMD(invalid monitor)");
574 return;
575 }
576
577 /* "It is an INVALID_OPERATION error for <data> to be NULL." */
578 if (data == NULL) {
579 _mesa_error(ctx, GL_INVALID_OPERATION,
580 "glGetPerfMonitorCounterDataAMD(data == NULL)");
581 return;
582 }
583
584 /* We need at least enough room for a single value. */
585 if (dataSize < sizeof(GLuint)) {
586 if (bytesWritten != NULL)
587 *bytesWritten = 0;
588 return;
589 }
590
591 /* If the monitor has never ended, there is no result. */
592 result_available = m->Ended &&
593 ctx->Driver.IsPerfMonitorResultAvailable(ctx, m);
594
595 /* AMD appears to return 0 for all queries unless a result is available. */
596 if (!result_available) {
597 *data = 0;
598 if (bytesWritten != NULL)
599 *bytesWritten = sizeof(GLuint);
600 return;
601 }
602
603 switch (pname) {
604 case GL_PERFMON_RESULT_AVAILABLE_AMD:
605 *data = 1;
606 if (bytesWritten != NULL)
607 *bytesWritten = sizeof(GLuint);
608 break;
609 case GL_PERFMON_RESULT_SIZE_AMD:
610 *data = perf_monitor_result_size(ctx, m);
611 if (bytesWritten != NULL)
612 *bytesWritten = sizeof(GLuint);
613 break;
614 case GL_PERFMON_RESULT_AMD:
615 ctx->Driver.GetPerfMonitorResult(ctx, m, dataSize, data, bytesWritten);
616 break;
617 default:
618 _mesa_error(ctx, GL_INVALID_ENUM,
619 "glGetPerfMonitorCounterDataAMD(pname)");
620 }
621 }
622
623 /**
624 * Returns how many bytes a counter's value takes up.
625 */
626 unsigned
627 _mesa_perf_monitor_counter_size(const struct gl_perf_monitor_counter *c)
628 {
629 switch (c->Type) {
630 case GL_FLOAT:
631 case GL_PERCENTAGE_AMD:
632 return sizeof(GLfloat);
633 case GL_UNSIGNED_INT:
634 return sizeof(GLuint);
635 case GL_UNSIGNED_INT64_AMD:
636 return sizeof(uint64_t);
637 default:
638 assert(!"Should not get here: invalid counter type");
639 return 0;
640 }
641 }