• 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(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