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