1 /*
2 * Copyright © 2012-2017 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_query.c
26 * Core Mesa support for the INTEL_performance_query extension.
27 */
28
29 #include <stdbool.h>
30 #include "glheader.h"
31 #include "context.h"
32 #include "enums.h"
33 #include "hash.h"
34 #include "macros.h"
35 #include "mtypes.h"
36 #include "performance_query.h"
37 #include "util/ralloc.h"
38
39 void
_mesa_init_performance_queries(struct gl_context * ctx)40 _mesa_init_performance_queries(struct gl_context *ctx)
41 {
42 ctx->PerfQuery.Objects = _mesa_NewHashTable();
43 }
44
45 static void
free_performance_query(void * data,void * user)46 free_performance_query(void *data, void *user)
47 {
48 struct gl_perf_query_object *m = data;
49 struct gl_context *ctx = user;
50
51 /* Don't confuse the implementation by deleting an active query. We can
52 * toggle Active/Used to false because we're tearing down the GL context
53 * and it's already idle (see _mesa_free_context_data).
54 */
55 m->Active = false;
56 m->Used = false;
57 ctx->Driver.DeletePerfQuery(ctx, m);
58 }
59
60 void
_mesa_free_performance_queries(struct gl_context * ctx)61 _mesa_free_performance_queries(struct gl_context *ctx)
62 {
63 _mesa_HashDeleteAll(ctx->PerfQuery.Objects,
64 free_performance_query, ctx);
65 _mesa_DeleteHashTable(ctx->PerfQuery.Objects);
66 }
67
68 static inline struct gl_perf_query_object *
lookup_object(struct gl_context * ctx,GLuint id)69 lookup_object(struct gl_context *ctx, GLuint id)
70 {
71 return _mesa_HashLookup(ctx->PerfQuery.Objects, id);
72 }
73
74 static GLuint
init_performance_query_info(struct gl_context * ctx)75 init_performance_query_info(struct gl_context *ctx)
76 {
77 if (ctx->Driver.InitPerfQueryInfo)
78 return ctx->Driver.InitPerfQueryInfo(ctx);
79 else
80 return 0;
81 }
82
83 /* For INTEL_performance_query, query id 0 is reserved to be invalid. */
84 static inline unsigned
queryid_to_index(GLuint queryid)85 queryid_to_index(GLuint queryid)
86 {
87 return queryid - 1;
88 }
89
90 static inline GLuint
index_to_queryid(unsigned index)91 index_to_queryid(unsigned index)
92 {
93 return index + 1;
94 }
95
96 static inline bool
queryid_valid(const struct gl_context * ctx,unsigned numQueries,GLuint queryid)97 queryid_valid(const struct gl_context *ctx, unsigned numQueries, GLuint queryid)
98 {
99 /* The GL_INTEL_performance_query spec says:
100 *
101 * "Performance counter ids values start with 1. Performance counter id 0
102 * is reserved as an invalid counter."
103 */
104 return queryid != 0 && queryid_to_index(queryid) < numQueries;
105 }
106
107 static inline GLuint
counterid_to_index(GLuint counterid)108 counterid_to_index(GLuint counterid)
109 {
110 return counterid - 1;
111 }
112
113 static void
output_clipped_string(GLchar * stringRet,GLuint stringMaxLen,const char * string)114 output_clipped_string(GLchar *stringRet,
115 GLuint stringMaxLen,
116 const char *string)
117 {
118 if (!stringRet)
119 return;
120
121 strncpy(stringRet, string ? string : "", stringMaxLen);
122
123 /* No specification given about whether returned strings needs
124 * to be zero-terminated. Zero-terminate the string always as we
125 * don't otherwise communicate the length of the returned
126 * string.
127 */
128 if (stringMaxLen > 0)
129 stringRet[stringMaxLen - 1] = '\0';
130 }
131
132 /*****************************************************************************/
133
134 extern void GLAPIENTRY
_mesa_GetFirstPerfQueryIdINTEL(GLuint * queryId)135 _mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId)
136 {
137 GET_CURRENT_CONTEXT(ctx);
138
139 unsigned numQueries;
140
141 /* The GL_INTEL_performance_query spec says:
142 *
143 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated."
144 */
145 if (!queryId) {
146 _mesa_error(ctx, GL_INVALID_VALUE,
147 "glGetFirstPerfQueryIdINTEL(queryId == NULL)");
148 return;
149 }
150
151 numQueries = init_performance_query_info(ctx);
152
153 /* The GL_INTEL_performance_query spec says:
154 *
155 * "If the given hardware platform doesn't support any performance
156 * queries, then the value of 0 is returned and INVALID_OPERATION error
157 * is raised."
158 */
159 if (numQueries == 0) {
160 *queryId = 0;
161 _mesa_error(ctx, GL_INVALID_OPERATION,
162 "glGetFirstPerfQueryIdINTEL(no queries supported)");
163 return;
164 }
165
166 *queryId = index_to_queryid(0);
167 }
168
169 extern void GLAPIENTRY
_mesa_GetNextPerfQueryIdINTEL(GLuint queryId,GLuint * nextQueryId)170 _mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId)
171 {
172 GET_CURRENT_CONTEXT(ctx);
173
174 unsigned numQueries;
175
176 /* The GL_INTEL_performance_query spec says:
177 *
178 * "The result is passed in location pointed by nextQueryId. If query
179 * identified by queryId is the last query available the value of 0 is
180 * returned. If the specified performance query identifier is invalid
181 * then INVALID_VALUE error is generated. If nextQueryId pointer is
182 * equal to 0, an INVALID_VALUE error is generated. Whenever error is
183 * generated, the value of 0 is returned."
184 */
185
186 if (!nextQueryId) {
187 _mesa_error(ctx, GL_INVALID_VALUE,
188 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)");
189 return;
190 }
191
192 numQueries = init_performance_query_info(ctx);
193
194 if (!queryid_valid(ctx, numQueries, queryId)) {
195 _mesa_error(ctx, GL_INVALID_VALUE,
196 "glGetNextPerfQueryIdINTEL(invalid query)");
197 return;
198 }
199
200 if (queryid_valid(ctx, numQueries, ++queryId))
201 *nextQueryId = queryId;
202 else
203 *nextQueryId = 0;
204 }
205
206 extern void GLAPIENTRY
_mesa_GetPerfQueryIdByNameINTEL(char * queryName,GLuint * queryId)207 _mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId)
208 {
209 GET_CURRENT_CONTEXT(ctx);
210
211 unsigned numQueries;
212 unsigned i;
213
214 /* The GL_INTEL_performance_query spec says:
215 *
216 * "If queryName does not reference a valid query name, an INVALID_VALUE
217 * error is generated."
218 */
219 if (!queryName) {
220 _mesa_error(ctx, GL_INVALID_VALUE,
221 "glGetPerfQueryIdByNameINTEL(queryName == NULL)");
222 return;
223 }
224
225 /* The specification does not state that this produces an error but
226 * to be consistent with glGetFirstPerfQueryIdINTEL we generate an
227 * INVALID_VALUE error
228 */
229 if (!queryId) {
230 _mesa_error(ctx, GL_INVALID_VALUE,
231 "glGetPerfQueryIdByNameINTEL(queryId == NULL)");
232 return;
233 }
234
235 numQueries = init_performance_query_info(ctx);
236
237 for (i = 0; i < numQueries; ++i) {
238 const GLchar *name;
239 GLuint ignore;
240
241 ctx->Driver.GetPerfQueryInfo(ctx, i, &name, &ignore, &ignore, &ignore);
242
243 if (strcmp(name, queryName) == 0) {
244 *queryId = index_to_queryid(i);
245 return;
246 }
247 }
248
249 _mesa_error(ctx, GL_INVALID_VALUE,
250 "glGetPerfQueryIdByNameINTEL(invalid query name)");
251 }
252
253 extern void GLAPIENTRY
_mesa_GetPerfQueryInfoINTEL(GLuint queryId,GLuint nameLength,GLchar * name,GLuint * dataSize,GLuint * numCounters,GLuint * numActive,GLuint * capsMask)254 _mesa_GetPerfQueryInfoINTEL(GLuint queryId,
255 GLuint nameLength, GLchar *name,
256 GLuint *dataSize,
257 GLuint *numCounters,
258 GLuint *numActive,
259 GLuint *capsMask)
260 {
261 GET_CURRENT_CONTEXT(ctx);
262
263 unsigned numQueries = init_performance_query_info(ctx);
264 unsigned queryIndex = queryid_to_index(queryId);
265 const char *queryName;
266 GLuint queryDataSize;
267 GLuint queryNumCounters;
268 GLuint queryNumActive;
269
270 if (!queryid_valid(ctx, numQueries, queryId)) {
271 /* The GL_INTEL_performance_query spec says:
272 *
273 * "If queryId does not reference a valid query type, an
274 * INVALID_VALUE error is generated."
275 */
276 _mesa_error(ctx, GL_INVALID_VALUE,
277 "glGetPerfQueryInfoINTEL(invalid query)");
278 return;
279 }
280
281 ctx->Driver.GetPerfQueryInfo(ctx, queryIndex,
282 &queryName,
283 &queryDataSize,
284 &queryNumCounters,
285 &queryNumActive);
286
287 output_clipped_string(name, nameLength, queryName);
288
289 if (dataSize)
290 *dataSize = queryDataSize;
291
292 if (numCounters)
293 *numCounters = queryNumCounters;
294
295 /* The GL_INTEL_performance_query spec says:
296 *
297 * "-- the actual number of already created query instances in
298 * maxInstances location"
299 *
300 * 1) Typo in the specification, should be noActiveInstances.
301 * 2) Another typo in the specification, maxInstances parameter is not listed
302 * in the declaration of this function in the list of new functions.
303 */
304 if (numActive)
305 *numActive = queryNumActive;
306
307 /* Assume for now that all queries are per-context */
308 if (capsMask)
309 *capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL;
310 }
311
312 extern void GLAPIENTRY
_mesa_GetPerfCounterInfoINTEL(GLuint queryId,GLuint counterId,GLuint nameLength,GLchar * name,GLuint descLength,GLchar * desc,GLuint * offset,GLuint * dataSize,GLuint * typeEnum,GLuint * dataTypeEnum,GLuint64 * rawCounterMaxValue)313 _mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId,
314 GLuint nameLength, GLchar *name,
315 GLuint descLength, GLchar *desc,
316 GLuint *offset,
317 GLuint *dataSize,
318 GLuint *typeEnum,
319 GLuint *dataTypeEnum,
320 GLuint64 *rawCounterMaxValue)
321 {
322 GET_CURRENT_CONTEXT(ctx);
323
324 unsigned numQueries = init_performance_query_info(ctx);
325 unsigned queryIndex = queryid_to_index(queryId);
326 const char *queryName;
327 GLuint queryDataSize;
328 GLuint queryNumCounters;
329 GLuint queryNumActive;
330 unsigned counterIndex;
331 const char *counterName;
332 const char *counterDesc;
333 GLuint counterOffset;
334 GLuint counterDataSize;
335 GLuint counterTypeEnum;
336 GLuint counterDataTypeEnum;
337 GLuint64 counterRawMax;
338
339 if (!queryid_valid(ctx, numQueries, queryId)) {
340 /* The GL_INTEL_performance_query spec says:
341 *
342 * "If the pair of queryId and counterId does not reference a valid
343 * counter, an INVALID_VALUE error is generated."
344 */
345 _mesa_error(ctx, GL_INVALID_VALUE,
346 "glGetPerfCounterInfoINTEL(invalid queryId)");
347 return;
348 }
349
350 ctx->Driver.GetPerfQueryInfo(ctx, queryIndex,
351 &queryName,
352 &queryDataSize,
353 &queryNumCounters,
354 &queryNumActive);
355
356 counterIndex = counterid_to_index(counterId);
357
358 if (counterIndex >= queryNumCounters) {
359 _mesa_error(ctx, GL_INVALID_VALUE,
360 "glGetPerfCounterInfoINTEL(invalid counterId)");
361 return;
362 }
363
364 ctx->Driver.GetPerfCounterInfo(ctx, queryIndex, counterIndex,
365 &counterName,
366 &counterDesc,
367 &counterOffset,
368 &counterDataSize,
369 &counterTypeEnum,
370 &counterDataTypeEnum,
371 &counterRawMax);
372
373 output_clipped_string(name, nameLength, counterName);
374 output_clipped_string(desc, descLength, counterDesc);
375
376 if (offset)
377 *offset = counterOffset;
378
379 if (dataSize)
380 *dataSize = counterDataSize;
381
382 if (typeEnum)
383 *typeEnum = counterTypeEnum;
384
385 if (dataTypeEnum)
386 *dataTypeEnum = counterDataTypeEnum;
387
388 if (rawCounterMaxValue)
389 *rawCounterMaxValue = counterRawMax;
390
391 if (rawCounterMaxValue) {
392 /* The GL_INTEL_performance_query spec says:
393 *
394 * "for some raw counters for which the maximal value is
395 * deterministic, the maximal value of the counter in 1 second is
396 * returned in the location pointed by rawCounterMaxValue, otherwise,
397 * the location is written with the value of 0."
398 *
399 * Since it's very useful to be able to report a maximum value for
400 * more that just counters using the _COUNTER_RAW_INTEL or
401 * _COUNTER_DURATION_RAW_INTEL enums (e.g. for a _THROUGHPUT tools
402 * want to be able to visualize the absolute throughput with respect
403 * to the theoretical maximum that's possible) and there doesn't seem
404 * to be any reason not to allow _THROUGHPUT counters to also be
405 * considerer "raw" here, we always leave it up to the backend to
406 * decide when it's appropriate to report a maximum counter value or 0
407 * if not.
408 */
409 *rawCounterMaxValue = counterRawMax;
410 }
411 }
412
413 extern void GLAPIENTRY
_mesa_CreatePerfQueryINTEL(GLuint queryId,GLuint * queryHandle)414 _mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle)
415 {
416 GET_CURRENT_CONTEXT(ctx);
417
418 unsigned numQueries = init_performance_query_info(ctx);
419 GLuint id;
420 struct gl_perf_query_object *obj;
421
422 /* The GL_INTEL_performance_query spec says:
423 *
424 * "If queryId does not reference a valid query type, an INVALID_VALUE
425 * error is generated."
426 */
427 if (!queryid_valid(ctx, numQueries, queryId)) {
428 _mesa_error(ctx, GL_INVALID_VALUE,
429 "glCreatePerfQueryINTEL(invalid queryId)");
430 return;
431 }
432
433 /* This is not specified in the extension, but is the only sane thing to
434 * do.
435 */
436 if (queryHandle == NULL) {
437 _mesa_error(ctx, GL_INVALID_VALUE,
438 "glCreatePerfQueryINTEL(queryHandle == NULL)");
439 return;
440 }
441
442 id = _mesa_HashFindFreeKeyBlock(ctx->PerfQuery.Objects, 1);
443 if (!id) {
444 /* The GL_INTEL_performance_query spec says:
445 *
446 * "If the query instance cannot be created due to exceeding the
447 * number of allowed instances or driver fails query creation due to
448 * an insufficient memory reason, an OUT_OF_MEMORY error is
449 * generated, and the location pointed by queryHandle returns NULL."
450 */
451 _mesa_error_no_memory(__func__);
452 return;
453 }
454
455 obj = ctx->Driver.NewPerfQueryObject(ctx, queryid_to_index(queryId));
456 if (obj == NULL) {
457 _mesa_error_no_memory(__func__);
458 return;
459 }
460
461 obj->Id = id;
462 obj->Active = false;
463 obj->Ready = false;
464
465 _mesa_HashInsert(ctx->PerfQuery.Objects, id, obj, true);
466 *queryHandle = id;
467 }
468
469 extern void GLAPIENTRY
_mesa_DeletePerfQueryINTEL(GLuint queryHandle)470 _mesa_DeletePerfQueryINTEL(GLuint queryHandle)
471 {
472 GET_CURRENT_CONTEXT(ctx);
473
474 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
475
476 /* The GL_INTEL_performance_query spec says:
477 *
478 * "If a query handle doesn't reference a previously created performance
479 * query instance, an INVALID_VALUE error is generated."
480 */
481 if (obj == NULL) {
482 _mesa_error(ctx, GL_INVALID_VALUE,
483 "glDeletePerfQueryINTEL(invalid queryHandle)");
484 return;
485 }
486
487 /* To avoid complications in the backend we never ask the backend to
488 * delete an active query or a query object while we are still
489 * waiting for data.
490 */
491
492 if (obj->Active)
493 _mesa_EndPerfQueryINTEL(queryHandle);
494
495 if (obj->Used && !obj->Ready) {
496 ctx->Driver.WaitPerfQuery(ctx, obj);
497 obj->Ready = true;
498 }
499
500 _mesa_HashRemove(ctx->PerfQuery.Objects, queryHandle);
501 ctx->Driver.DeletePerfQuery(ctx, obj);
502 }
503
504 extern void GLAPIENTRY
_mesa_BeginPerfQueryINTEL(GLuint queryHandle)505 _mesa_BeginPerfQueryINTEL(GLuint queryHandle)
506 {
507 GET_CURRENT_CONTEXT(ctx);
508
509 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
510
511 /* The GL_INTEL_performance_query spec says:
512 *
513 * "If a query handle doesn't reference a previously created performance
514 * query instance, an INVALID_VALUE error is generated."
515 */
516 if (obj == NULL) {
517 _mesa_error(ctx, GL_INVALID_VALUE,
518 "glBeginPerfQueryINTEL(invalid queryHandle)");
519 return;
520 }
521
522 /* The GL_INTEL_performance_query spec says:
523 *
524 * "Note that some query types, they cannot be collected in the same
525 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
526 * they refer to queries of such different types. In such case
527 * INVALID_OPERATION error is generated."
528 *
529 * We also generate an INVALID_OPERATION error if the driver can't begin
530 * a query for its own reasons, and for nesting the same query.
531 */
532 if (obj->Active) {
533 _mesa_error(ctx, GL_INVALID_OPERATION,
534 "glBeginPerfQueryINTEL(already active)");
535 return;
536 }
537
538 /* To avoid complications in the backend we never ask the backend to
539 * reuse a query object and begin a new query while we are still
540 * waiting for data on that object.
541 */
542 if (obj->Used && !obj->Ready) {
543 ctx->Driver.WaitPerfQuery(ctx, obj);
544 obj->Ready = true;
545 }
546
547 if (ctx->Driver.BeginPerfQuery(ctx, obj)) {
548 obj->Used = true;
549 obj->Active = true;
550 obj->Ready = false;
551 } else {
552 _mesa_error(ctx, GL_INVALID_OPERATION,
553 "glBeginPerfQueryINTEL(driver unable to begin query)");
554 }
555 }
556
557 extern void GLAPIENTRY
_mesa_EndPerfQueryINTEL(GLuint queryHandle)558 _mesa_EndPerfQueryINTEL(GLuint queryHandle)
559 {
560 GET_CURRENT_CONTEXT(ctx);
561
562 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
563
564 /* Not explicitly covered in the spec, but for consistency... */
565 if (obj == NULL) {
566 _mesa_error(ctx, GL_INVALID_VALUE,
567 "glEndPerfQueryINTEL(invalid queryHandle)");
568 return;
569 }
570
571 /* The GL_INTEL_performance_query spec says:
572 *
573 * "If a performance query is not currently started, an
574 * INVALID_OPERATION error will be generated."
575 */
576
577 if (!obj->Active) {
578 _mesa_error(ctx, GL_INVALID_OPERATION,
579 "glEndPerfQueryINTEL(not active)");
580 return;
581 }
582
583 ctx->Driver.EndPerfQuery(ctx, obj);
584
585 obj->Active = false;
586 obj->Ready = false;
587 }
588
589 extern void GLAPIENTRY
_mesa_GetPerfQueryDataINTEL(GLuint queryHandle,GLuint flags,GLsizei dataSize,void * data,GLuint * bytesWritten)590 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
591 GLsizei dataSize, void *data, GLuint *bytesWritten)
592 {
593 GET_CURRENT_CONTEXT(ctx);
594
595 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
596
597 /* Not explicitly covered in the spec, but for consistency... */
598 if (obj == NULL) {
599 _mesa_error(ctx, GL_INVALID_VALUE,
600 "glEndPerfQueryINTEL(invalid queryHandle)");
601 return;
602 }
603
604 /* The GL_INTEL_performance_query spec says:
605 *
606 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE
607 * error is generated."
608 */
609 if (!bytesWritten || !data) {
610 _mesa_error(ctx, GL_INVALID_VALUE,
611 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
612 return;
613 }
614
615 /* Just for good measure in case a lazy application is only
616 * checking this and not checking for errors...
617 */
618 *bytesWritten = 0;
619
620 /* Not explicitly covered in the spec but a query that was never started
621 * cannot return any data.
622 */
623 if (!obj->Used) {
624 _mesa_error(ctx, GL_INVALID_OPERATION,
625 "glGetPerfQueryDataINTEL(query never began)");
626 return;
627 }
628
629 /* Not explicitly covered in the spec but to be consistent with
630 * EndPerfQuery which validates that an application only ends an
631 * active query we also validate that an application doesn't try
632 * and get the data for a still active query...
633 */
634 if (obj->Active) {
635 _mesa_error(ctx, GL_INVALID_OPERATION,
636 "glGetPerfQueryDataINTEL(query still active)");
637 return;
638 }
639
640 obj->Ready = ctx->Driver.IsPerfQueryReady(ctx, obj);
641
642 if (!obj->Ready) {
643 if (flags == GL_PERFQUERY_FLUSH_INTEL) {
644 ctx->Driver.Flush(ctx);
645 } else if (flags == GL_PERFQUERY_WAIT_INTEL) {
646 ctx->Driver.WaitPerfQuery(ctx, obj);
647 obj->Ready = true;
648 }
649 }
650
651 if (obj->Ready)
652 ctx->Driver.GetPerfQueryData(ctx, obj, dataSize, data, bytesWritten);
653 }
654