xref: /aosp_15_r20/external/mesa3d/src/mesa/main/performance_query.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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 "util/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 #include "api_exec_decl.h"
39 
40 #include "pipe/p_context.h"
41 
42 #include "state_tracker/st_cb_flush.h"
43 
44 void
_mesa_init_performance_queries(struct gl_context * ctx)45 _mesa_init_performance_queries(struct gl_context *ctx)
46 {
47    _mesa_InitHashTable(&ctx->PerfQuery.Objects);
48 }
49 
50 static void
free_performance_query(void * data,void * user)51 free_performance_query(void *data, void *user)
52 {
53    struct gl_perf_query_object *m = data;
54    struct gl_context *ctx = user;
55 
56    /* Don't confuse the implementation by deleting an active query. We can
57     * toggle Active/Used to false because we're tearing down the GL context
58     * and it's already idle (see _mesa_free_context_data).
59     */
60    m->Active = false;
61    m->Used = false;
62    ctx->pipe->delete_intel_perf_query(ctx->pipe, (struct pipe_query *)m);
63 }
64 
65 void
_mesa_free_performance_queries(struct gl_context * ctx)66 _mesa_free_performance_queries(struct gl_context *ctx)
67 {
68    _mesa_DeinitHashTable(&ctx->PerfQuery.Objects, free_performance_query, ctx);
69 }
70 
71 static inline struct gl_perf_query_object *
lookup_object(struct gl_context * ctx,GLuint id)72 lookup_object(struct gl_context *ctx, GLuint id)
73 {
74    return _mesa_HashLookup(&ctx->PerfQuery.Objects, id);
75 }
76 
77 static GLuint
init_performance_query_info(struct gl_context * ctx)78 init_performance_query_info(struct gl_context *ctx)
79 {
80    return ctx->pipe->init_intel_perf_query_info(ctx->pipe);
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->pipe->get_intel_perf_query_info(ctx->pipe, i, &name,
242                                            &ignore, &ignore, &ignore);
243 
244       if (strcmp(name, queryName) == 0) {
245          *queryId = index_to_queryid(i);
246          return;
247       }
248    }
249 
250    _mesa_error(ctx, GL_INVALID_VALUE,
251                "glGetPerfQueryIdByNameINTEL(invalid query name)");
252 }
253 
254 extern void GLAPIENTRY
_mesa_GetPerfQueryInfoINTEL(GLuint queryId,GLuint nameLength,GLchar * name,GLuint * dataSize,GLuint * numCounters,GLuint * numActive,GLuint * capsMask)255 _mesa_GetPerfQueryInfoINTEL(GLuint queryId,
256                             GLuint nameLength, GLchar *name,
257                             GLuint *dataSize,
258                             GLuint *numCounters,
259                             GLuint *numActive,
260                             GLuint *capsMask)
261 {
262    GET_CURRENT_CONTEXT(ctx);
263 
264    unsigned numQueries = init_performance_query_info(ctx);
265    unsigned queryIndex = queryid_to_index(queryId);
266    const char *queryName;
267    GLuint queryDataSize;
268    GLuint queryNumCounters;
269    GLuint queryNumActive;
270 
271    if (!queryid_valid(ctx, numQueries, queryId)) {
272       /* The GL_INTEL_performance_query spec says:
273        *
274        *    "If queryId does not reference a valid query type, an
275        *    INVALID_VALUE error is generated."
276        */
277       _mesa_error(ctx, GL_INVALID_VALUE,
278                   "glGetPerfQueryInfoINTEL(invalid query)");
279       return;
280    }
281 
282    ctx->pipe->get_intel_perf_query_info(ctx->pipe, queryIndex, &queryName,
283                                         &queryDataSize, &queryNumCounters,
284                                         &queryNumActive);
285 
286    output_clipped_string(name, nameLength, queryName);
287 
288    if (dataSize)
289       *dataSize = queryDataSize;
290 
291    if (numCounters)
292       *numCounters = queryNumCounters;
293 
294    /* The GL_INTEL_performance_query spec says:
295     *
296     *    "-- the actual number of already created query instances in
297     *    maxInstances location"
298     *
299     * 1) Typo in the specification, should be noActiveInstances.
300     * 2) Another typo in the specification, maxInstances parameter is not listed
301     *    in the declaration of this function in the list of new functions.
302     */
303    if (numActive)
304       *numActive = queryNumActive;
305 
306    /* Assume for now that all queries are per-context */
307    if (capsMask)
308       *capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL;
309 }
310 
311 static uint32_t
pipe_counter_type_enum_to_gl_type(enum pipe_perf_counter_type type)312 pipe_counter_type_enum_to_gl_type(enum pipe_perf_counter_type type)
313 {
314    switch (type) {
315    case PIPE_PERF_COUNTER_TYPE_EVENT: return GL_PERFQUERY_COUNTER_EVENT_INTEL;
316    case PIPE_PERF_COUNTER_TYPE_DURATION_NORM: return GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL;
317    case PIPE_PERF_COUNTER_TYPE_DURATION_RAW: return GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL;
318    case PIPE_PERF_COUNTER_TYPE_THROUGHPUT: return GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL;
319    case PIPE_PERF_COUNTER_TYPE_RAW: return GL_PERFQUERY_COUNTER_RAW_INTEL;
320    case PIPE_PERF_COUNTER_TYPE_TIMESTAMP: return GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL;
321    default:
322       unreachable("Unknown counter type");
323    }
324 }
325 
326 static uint32_t
pipe_counter_data_type_to_gl_type(enum pipe_perf_counter_data_type type)327 pipe_counter_data_type_to_gl_type(enum pipe_perf_counter_data_type type)
328 {
329    switch (type) {
330    case PIPE_PERF_COUNTER_DATA_TYPE_BOOL32: return GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL;
331    case PIPE_PERF_COUNTER_DATA_TYPE_UINT32: return GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL;
332    case PIPE_PERF_COUNTER_DATA_TYPE_UINT64: return GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL;
333    case PIPE_PERF_COUNTER_DATA_TYPE_FLOAT: return GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL;
334    case PIPE_PERF_COUNTER_DATA_TYPE_DOUBLE: return GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL;
335    default:
336       unreachable("Unknown counter data type");
337    }
338 }
339 
340 static void
get_perf_counter_info(struct gl_context * ctx,unsigned query_index,unsigned counter_index,const char ** name,const char ** desc,GLuint * offset,GLuint * data_size,GLuint * type_enum,GLuint * data_type_enum,GLuint64 * raw_max)341 get_perf_counter_info(struct gl_context *ctx,
342                       unsigned query_index,
343                       unsigned counter_index,
344                       const char **name,
345                       const char **desc,
346                       GLuint *offset,
347                       GLuint *data_size,
348                       GLuint *type_enum,
349                       GLuint *data_type_enum,
350                       GLuint64 *raw_max)
351 {
352    struct pipe_context *pipe = ctx->pipe;
353    uint32_t pipe_type_enum;
354    uint32_t pipe_data_type_enum;
355 
356    pipe->get_intel_perf_query_counter_info(pipe, query_index, counter_index,
357                                            name, desc, offset, data_size,
358                                            &pipe_type_enum, &pipe_data_type_enum, raw_max);
359    *type_enum = pipe_counter_type_enum_to_gl_type(pipe_type_enum);
360    *data_type_enum = pipe_counter_data_type_to_gl_type(pipe_data_type_enum);
361 }
362 
363 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)364 _mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId,
365                               GLuint nameLength, GLchar *name,
366                               GLuint descLength, GLchar *desc,
367                               GLuint *offset,
368                               GLuint *dataSize,
369                               GLuint *typeEnum,
370                               GLuint *dataTypeEnum,
371                               GLuint64 *rawCounterMaxValue)
372 {
373    GET_CURRENT_CONTEXT(ctx);
374 
375    unsigned numQueries = init_performance_query_info(ctx);
376    unsigned queryIndex = queryid_to_index(queryId);
377    const char *queryName;
378    GLuint queryDataSize;
379    GLuint queryNumCounters;
380    GLuint queryNumActive;
381    unsigned counterIndex;
382    const char *counterName;
383    const char *counterDesc;
384    GLuint counterOffset;
385    GLuint counterDataSize;
386    GLuint counterTypeEnum;
387    GLuint counterDataTypeEnum;
388    GLuint64 counterRawMax;
389 
390    if (!queryid_valid(ctx, numQueries, queryId)) {
391       /* The GL_INTEL_performance_query spec says:
392        *
393        *    "If the pair of queryId and counterId does not reference a valid
394        *    counter, an INVALID_VALUE error is generated."
395        */
396       _mesa_error(ctx, GL_INVALID_VALUE,
397                   "glGetPerfCounterInfoINTEL(invalid queryId)");
398       return;
399    }
400 
401    ctx->pipe->get_intel_perf_query_info(ctx->pipe, queryIndex, &queryName,
402                                         &queryDataSize, &queryNumCounters,
403                                         &queryNumActive);
404 
405    counterIndex = counterid_to_index(counterId);
406 
407    if (counterIndex >= queryNumCounters) {
408       _mesa_error(ctx, GL_INVALID_VALUE,
409                   "glGetPerfCounterInfoINTEL(invalid counterId)");
410       return;
411    }
412 
413    get_perf_counter_info(ctx, queryIndex, counterIndex,
414                          &counterName,
415                          &counterDesc,
416                          &counterOffset,
417                          &counterDataSize,
418                          &counterTypeEnum,
419                          &counterDataTypeEnum,
420                          &counterRawMax);
421 
422    output_clipped_string(name, nameLength, counterName);
423    output_clipped_string(desc, descLength, counterDesc);
424 
425    if (offset)
426       *offset = counterOffset;
427 
428    if (dataSize)
429       *dataSize = counterDataSize;
430 
431    if (typeEnum)
432       *typeEnum = counterTypeEnum;
433 
434    if (dataTypeEnum)
435       *dataTypeEnum = counterDataTypeEnum;
436 
437    if (rawCounterMaxValue)
438       *rawCounterMaxValue = counterRawMax;
439 
440    if (rawCounterMaxValue) {
441       /* The GL_INTEL_performance_query spec says:
442        *
443        *    "for some raw counters for which the maximal value is
444        *    deterministic, the maximal value of the counter in 1 second is
445        *    returned in the location pointed by rawCounterMaxValue, otherwise,
446        *    the location is written with the value of 0."
447        *
448        *    Since it's very useful to be able to report a maximum value for
449        *    more that just counters using the _COUNTER_RAW_INTEL or
450        *    _COUNTER_DURATION_RAW_INTEL enums (e.g. for a _THROUGHPUT tools
451        *    want to be able to visualize the absolute throughput with respect
452        *    to the theoretical maximum that's possible) and there doesn't seem
453        *    to be any reason not to allow _THROUGHPUT counters to also be
454        *    considerer "raw" here, we always leave it up to the backend to
455        *    decide when it's appropriate to report a maximum counter value or 0
456        *    if not.
457        */
458       *rawCounterMaxValue = counterRawMax;
459    }
460 }
461 
462 extern void GLAPIENTRY
_mesa_CreatePerfQueryINTEL(GLuint queryId,GLuint * queryHandle)463 _mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle)
464 {
465    GET_CURRENT_CONTEXT(ctx);
466 
467    unsigned numQueries = init_performance_query_info(ctx);
468    GLuint id;
469    struct gl_perf_query_object *obj;
470 
471    /* The GL_INTEL_performance_query spec says:
472     *
473     *    "If queryId does not reference a valid query type, an INVALID_VALUE
474     *    error is generated."
475     */
476    if (!queryid_valid(ctx, numQueries, queryId)) {
477       _mesa_error(ctx, GL_INVALID_VALUE,
478                   "glCreatePerfQueryINTEL(invalid queryId)");
479       return;
480    }
481 
482    /* This is not specified in the extension, but is the only sane thing to
483     * do.
484     */
485    if (queryHandle == NULL) {
486       _mesa_error(ctx, GL_INVALID_VALUE,
487                   "glCreatePerfQueryINTEL(queryHandle == NULL)");
488       return;
489    }
490 
491    id = _mesa_HashFindFreeKeyBlock(&ctx->PerfQuery.Objects, 1);
492    if (!id) {
493       /* The GL_INTEL_performance_query spec says:
494        *
495        *    "If the query instance cannot be created due to exceeding the
496        *    number of allowed instances or driver fails query creation due to
497        *    an insufficient memory reason, an OUT_OF_MEMORY error is
498        *    generated, and the location pointed by queryHandle returns NULL."
499        */
500       _mesa_error_no_memory(__func__);
501       return;
502    }
503 
504    obj = (struct gl_perf_query_object *)ctx->pipe->new_intel_perf_query_obj(ctx->pipe,
505                                                                             queryid_to_index(queryId));
506    if (obj == NULL) {
507       _mesa_error_no_memory(__func__);
508       return;
509    }
510 
511    obj->Id = id;
512    obj->Active = false;
513    obj->Ready = false;
514 
515    _mesa_HashInsert(&ctx->PerfQuery.Objects, id, obj);
516    *queryHandle = id;
517 }
518 
519 extern void GLAPIENTRY
_mesa_DeletePerfQueryINTEL(GLuint queryHandle)520 _mesa_DeletePerfQueryINTEL(GLuint queryHandle)
521 {
522    GET_CURRENT_CONTEXT(ctx);
523 
524    struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
525 
526    /* The GL_INTEL_performance_query spec says:
527     *
528     *    "If a query handle doesn't reference a previously created performance
529     *    query instance, an INVALID_VALUE error is generated."
530     */
531    if (obj == NULL) {
532       _mesa_error(ctx, GL_INVALID_VALUE,
533                   "glDeletePerfQueryINTEL(invalid queryHandle)");
534       return;
535    }
536 
537    /* To avoid complications in the backend we never ask the backend to
538     * delete an active query or a query object while we are still
539     * waiting for data.
540     */
541 
542    if (obj->Active)
543       _mesa_EndPerfQueryINTEL(queryHandle);
544 
545    if (obj->Used && !obj->Ready) {
546       ctx->pipe->wait_intel_perf_query(ctx->pipe, (struct pipe_query *)obj);
547       obj->Ready = true;
548    }
549 
550    _mesa_HashRemove(&ctx->PerfQuery.Objects, queryHandle);
551    ctx->pipe->delete_intel_perf_query(ctx->pipe, (struct pipe_query *)obj);
552 }
553 
554 extern void GLAPIENTRY
_mesa_BeginPerfQueryINTEL(GLuint queryHandle)555 _mesa_BeginPerfQueryINTEL(GLuint queryHandle)
556 {
557    GET_CURRENT_CONTEXT(ctx);
558 
559    struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
560 
561    /* The GL_INTEL_performance_query spec says:
562     *
563     *    "If a query handle doesn't reference a previously created performance
564     *    query instance, an INVALID_VALUE error is generated."
565     */
566    if (obj == NULL) {
567       _mesa_error(ctx, GL_INVALID_VALUE,
568                   "glBeginPerfQueryINTEL(invalid queryHandle)");
569       return;
570    }
571 
572    /* The GL_INTEL_performance_query spec says:
573     *
574     *    "Note that some query types, they cannot be collected in the same
575     *    time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if
576     *    they refer to queries of such different types. In such case
577     *    INVALID_OPERATION error is generated."
578     *
579     * We also generate an INVALID_OPERATION error if the driver can't begin
580     * a query for its own reasons, and for nesting the same query.
581     */
582    if (obj->Active) {
583       _mesa_error(ctx, GL_INVALID_OPERATION,
584                   "glBeginPerfQueryINTEL(already active)");
585       return;
586    }
587 
588    /* To avoid complications in the backend we never ask the backend to
589     * reuse a query object and begin a new query while we are still
590     * waiting for data on that object.
591     */
592    if (obj->Used && !obj->Ready) {
593       ctx->pipe->wait_intel_perf_query(ctx->pipe, (struct pipe_query *)obj);
594       obj->Ready = true;
595    }
596 
597    if (ctx->pipe->begin_intel_perf_query(ctx->pipe, (struct pipe_query *)obj)) {
598       obj->Used = true;
599       obj->Active = true;
600       obj->Ready = false;
601    } else {
602       _mesa_error(ctx, GL_INVALID_OPERATION,
603                   "glBeginPerfQueryINTEL(driver unable to begin query)");
604    }
605 }
606 
607 extern void GLAPIENTRY
_mesa_EndPerfQueryINTEL(GLuint queryHandle)608 _mesa_EndPerfQueryINTEL(GLuint queryHandle)
609 {
610    GET_CURRENT_CONTEXT(ctx);
611 
612    struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
613 
614    /* Not explicitly covered in the spec, but for consistency... */
615    if (obj == NULL) {
616       _mesa_error(ctx, GL_INVALID_VALUE,
617                   "glEndPerfQueryINTEL(invalid queryHandle)");
618       return;
619    }
620 
621    /* The GL_INTEL_performance_query spec says:
622     *
623     *    "If a performance query is not currently started, an
624     *    INVALID_OPERATION error will be generated."
625     */
626 
627    if (!obj->Active) {
628       _mesa_error(ctx, GL_INVALID_OPERATION,
629                   "glEndPerfQueryINTEL(not active)");
630       return;
631    }
632 
633    ctx->pipe->end_intel_perf_query(ctx->pipe, (struct pipe_query *)obj);
634 
635    obj->Active = false;
636    obj->Ready = false;
637 }
638 
639 extern void GLAPIENTRY
_mesa_GetPerfQueryDataINTEL(GLuint queryHandle,GLuint flags,GLsizei dataSize,void * data,GLuint * bytesWritten)640 _mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags,
641                             GLsizei dataSize, void *data, GLuint *bytesWritten)
642 {
643    GET_CURRENT_CONTEXT(ctx);
644 
645    struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle);
646 
647    /* Not explicitly covered in the spec, but for consistency... */
648    if (obj == NULL) {
649       _mesa_error(ctx, GL_INVALID_VALUE,
650                   "glEndPerfQueryINTEL(invalid queryHandle)");
651       return;
652    }
653 
654    /* The GL_INTEL_performance_query spec says:
655     *
656     *    "If bytesWritten or data pointers are NULL then an INVALID_VALUE
657     *    error is generated."
658     */
659    if (!bytesWritten || !data) {
660       _mesa_error(ctx, GL_INVALID_VALUE,
661                   "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)");
662       return;
663    }
664 
665    /* Just for good measure in case a lazy application is only
666     * checking this and not checking for errors...
667     */
668    *bytesWritten = 0;
669 
670    /* Not explicitly covered in the spec but a query that was never started
671     * cannot return any data.
672     */
673    if (!obj->Used) {
674       _mesa_error(ctx, GL_INVALID_OPERATION,
675                   "glGetPerfQueryDataINTEL(query never began)");
676       return;
677    }
678 
679    /* Not explicitly covered in the spec but to be consistent with
680     * EndPerfQuery which validates that an application only ends an
681     * active query we also validate that an application doesn't try
682     * and get the data for a still active query...
683     */
684    if (obj->Active) {
685       _mesa_error(ctx, GL_INVALID_OPERATION,
686                   "glGetPerfQueryDataINTEL(query still active)");
687       return;
688    }
689 
690    if (!obj->Ready)
691       obj->Ready = ctx->pipe->is_intel_perf_query_ready(ctx->pipe,
692                                                         (struct pipe_query *)obj);
693 
694    if (!obj->Ready) {
695       if (flags == GL_PERFQUERY_FLUSH_INTEL) {
696          st_glFlush(ctx, 0);
697       } else if (flags == GL_PERFQUERY_WAIT_INTEL) {
698          ctx->pipe->wait_intel_perf_query(ctx->pipe, (struct pipe_query *)obj);
699          obj->Ready = true;
700       }
701    }
702 
703    if (obj->Ready) {
704       if (!ctx->pipe->get_intel_perf_query_data(ctx->pipe, (struct pipe_query *)obj,
705                                                 dataSize, data, bytesWritten)) {
706          memset(data, 0, dataSize);
707          *bytesWritten = 0;
708 
709          _mesa_error(ctx, GL_INVALID_OPERATION,
710                      "glGetPerfQueryDataINTEL(deferred begin query failure)");
711       }
712    }
713 }
714