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