Merge remote branch 'origin/master' into pipe-video
[mesa.git] / src / mesa / main / queryobj.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 7.1
4 *
5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 #include "glheader.h"
27 #include "context.h"
28 #include "enums.h"
29 #include "hash.h"
30 #include "imports.h"
31 #include "queryobj.h"
32 #include "mfeatures.h"
33 #include "mtypes.h"
34 #include "main/dispatch.h"
35
36
37 #if FEATURE_queryobj
38
39
40 /**
41 * Allocate a new query object. This is a fallback routine called via
42 * ctx->Driver.NewQueryObject().
43 * \param ctx - rendering context
44 * \param id - the new object's ID
45 * \return pointer to new query_object object or NULL if out of memory.
46 */
47 static struct gl_query_object *
48 _mesa_new_query_object(struct gl_context *ctx, GLuint id)
49 {
50 struct gl_query_object *q = MALLOC_STRUCT(gl_query_object);
51 (void) ctx;
52 if (q) {
53 q->Id = id;
54 q->Result = 0;
55 q->Active = GL_FALSE;
56 q->Ready = GL_TRUE; /* correct, see spec */
57 }
58 return q;
59 }
60
61
62 /**
63 * Begin a query. Software driver fallback.
64 * Called via ctx->Driver.BeginQuery().
65 */
66 static void
67 _mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q)
68 {
69 /* no-op */
70 }
71
72
73 /**
74 * End a query. Software driver fallback.
75 * Called via ctx->Driver.EndQuery().
76 */
77 static void
78 _mesa_end_query(struct gl_context *ctx, struct gl_query_object *q)
79 {
80 q->Ready = GL_TRUE;
81 }
82
83
84 /**
85 * Wait for query to complete. Software driver fallback.
86 * Called via ctx->Driver.WaitQuery().
87 */
88 static void
89 _mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q)
90 {
91 /* For software drivers, _mesa_end_query() should have completed the query.
92 * For real hardware, implement a proper WaitQuery() driver function,
93 * which may require issuing a flush.
94 */
95 assert(q->Ready);
96 }
97
98
99 /**
100 * Check if a query results are ready. Software driver fallback.
101 * Called via ctx->Driver.CheckQuery().
102 */
103 static void
104 _mesa_check_query(struct gl_context *ctx, struct gl_query_object *q)
105 {
106 /* No-op for sw rendering.
107 * HW drivers may need to flush at this time.
108 */
109 }
110
111
112 /**
113 * Delete a query object. Called via ctx->Driver.DeleteQuery().
114 * Not removed from hash table here.
115 */
116 static void
117 _mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q)
118 {
119 free(q);
120 }
121
122
123 void
124 _mesa_init_query_object_functions(struct dd_function_table *driver)
125 {
126 driver->NewQueryObject = _mesa_new_query_object;
127 driver->DeleteQuery = _mesa_delete_query;
128 driver->BeginQuery = _mesa_begin_query;
129 driver->EndQuery = _mesa_end_query;
130 driver->WaitQuery = _mesa_wait_query;
131 driver->CheckQuery = _mesa_check_query;
132 }
133
134
135 /**
136 * Return pointer to the query object binding point for the given target.
137 * \return NULL if invalid target, else the address of binding point
138 */
139 static struct gl_query_object **
140 get_query_binding_point(struct gl_context *ctx, GLenum target)
141 {
142 switch (target) {
143 case GL_SAMPLES_PASSED_ARB:
144 if (ctx->Extensions.ARB_occlusion_query)
145 return &ctx->Query.CurrentOcclusionObject;
146 else
147 return NULL;
148 case GL_ANY_SAMPLES_PASSED:
149 if (ctx->Extensions.ARB_occlusion_query2)
150 return &ctx->Query.CurrentOcclusionObject;
151 else
152 return NULL;
153 case GL_TIME_ELAPSED_EXT:
154 if (ctx->Extensions.EXT_timer_query)
155 return &ctx->Query.CurrentTimerObject;
156 else
157 return NULL;
158 #if FEATURE_EXT_transform_feedback
159 case GL_PRIMITIVES_GENERATED:
160 if (ctx->Extensions.EXT_transform_feedback)
161 return &ctx->Query.PrimitivesGenerated;
162 else
163 return NULL;
164 case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
165 if (ctx->Extensions.EXT_transform_feedback)
166 return &ctx->Query.PrimitivesWritten;
167 else
168 return NULL;
169 #endif
170 default:
171 return NULL;
172 }
173 }
174
175
176 void GLAPIENTRY
177 _mesa_GenQueriesARB(GLsizei n, GLuint *ids)
178 {
179 GLuint first;
180 GET_CURRENT_CONTEXT(ctx);
181 ASSERT_OUTSIDE_BEGIN_END(ctx);
182
183 if (MESA_VERBOSE & VERBOSE_API)
184 _mesa_debug(ctx, "glGenQueries(%d)\n", n);
185
186 if (n < 0) {
187 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
188 return;
189 }
190
191 /* No query objects can be active at this time! */
192 if (ctx->Query.CurrentOcclusionObject ||
193 ctx->Query.CurrentTimerObject) {
194 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
195 return;
196 }
197
198 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
199 if (first) {
200 GLsizei i;
201 for (i = 0; i < n; i++) {
202 struct gl_query_object *q
203 = ctx->Driver.NewQueryObject(ctx, first + i);
204 if (!q) {
205 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
206 return;
207 }
208 ids[i] = first + i;
209 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
210 }
211 }
212 }
213
214
215 void GLAPIENTRY
216 _mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
217 {
218 GLint i;
219 GET_CURRENT_CONTEXT(ctx);
220 ASSERT_OUTSIDE_BEGIN_END(ctx);
221
222 if (MESA_VERBOSE & VERBOSE_API)
223 _mesa_debug(ctx, "glDeleeteQueries(%d)\n", n);
224
225 if (n < 0) {
226 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
227 return;
228 }
229
230 /* No query objects can be active at this time! */
231 if (ctx->Query.CurrentOcclusionObject ||
232 ctx->Query.CurrentTimerObject) {
233 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
234 return;
235 }
236
237 for (i = 0; i < n; i++) {
238 if (ids[i] > 0) {
239 struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]);
240 if (q) {
241 ASSERT(!q->Active); /* should be caught earlier */
242 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
243 ctx->Driver.DeleteQuery(ctx, q);
244 }
245 }
246 }
247 }
248
249
250 GLboolean GLAPIENTRY
251 _mesa_IsQueryARB(GLuint id)
252 {
253 GET_CURRENT_CONTEXT(ctx);
254 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
255
256 if (MESA_VERBOSE & VERBOSE_API)
257 _mesa_debug(ctx, "glIsQuery(%u)\n", id);
258
259 if (id && _mesa_lookup_query_object(ctx, id))
260 return GL_TRUE;
261 else
262 return GL_FALSE;
263 }
264
265
266 static void GLAPIENTRY
267 _mesa_BeginQueryARB(GLenum target, GLuint id)
268 {
269 struct gl_query_object *q, **bindpt;
270 GET_CURRENT_CONTEXT(ctx);
271 ASSERT_OUTSIDE_BEGIN_END(ctx);
272
273 if (MESA_VERBOSE & VERBOSE_API)
274 _mesa_debug(ctx, "glBeginQuery(%s, %u)\n",
275 _mesa_lookup_enum_by_nr(target), id);
276
277 FLUSH_VERTICES(ctx, _NEW_DEPTH);
278
279 bindpt = get_query_binding_point(ctx, target);
280 if (!bindpt) {
281 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
282 return;
283 }
284
285 if (id == 0) {
286 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
287 return;
288 }
289
290 q = _mesa_lookup_query_object(ctx, id);
291 if (!q) {
292 /* create new object */
293 q = ctx->Driver.NewQueryObject(ctx, id);
294 if (!q) {
295 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
296 return;
297 }
298 _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
299 }
300 else {
301 /* pre-existing object */
302 if (q->Active) {
303 _mesa_error(ctx, GL_INVALID_OPERATION,
304 "glBeginQueryARB(query already active)");
305 return;
306 }
307 }
308
309 q->Target = target;
310 q->Active = GL_TRUE;
311 q->Result = 0;
312 q->Ready = GL_FALSE;
313
314 /* XXX should probably refcount query objects */
315 *bindpt = q;
316
317 ctx->Driver.BeginQuery(ctx, q);
318 }
319
320
321 static void GLAPIENTRY
322 _mesa_EndQueryARB(GLenum target)
323 {
324 struct gl_query_object *q, **bindpt;
325 GET_CURRENT_CONTEXT(ctx);
326 ASSERT_OUTSIDE_BEGIN_END(ctx);
327
328 if (MESA_VERBOSE & VERBOSE_API)
329 _mesa_debug(ctx, "glEndQuery(%s)\n", _mesa_lookup_enum_by_nr(target));
330
331 FLUSH_VERTICES(ctx, _NEW_DEPTH);
332
333 bindpt = get_query_binding_point(ctx, target);
334 if (!bindpt) {
335 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
336 return;
337 }
338
339 /* XXX should probably refcount query objects */
340 q = *bindpt;
341 *bindpt = NULL;
342
343 if (!q || !q->Active) {
344 _mesa_error(ctx, GL_INVALID_OPERATION,
345 "glEndQueryARB(no matching glBeginQueryARB)");
346 return;
347 }
348
349 q->Active = GL_FALSE;
350 ctx->Driver.EndQuery(ctx, q);
351 }
352
353
354 void GLAPIENTRY
355 _mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
356 {
357 struct gl_query_object *q, **bindpt;
358 GET_CURRENT_CONTEXT(ctx);
359 ASSERT_OUTSIDE_BEGIN_END(ctx);
360
361 if (MESA_VERBOSE & VERBOSE_API)
362 _mesa_debug(ctx, "glGetQueryiv(%s, %s)\n",
363 _mesa_lookup_enum_by_nr(target),
364 _mesa_lookup_enum_by_nr(pname));
365
366 bindpt = get_query_binding_point(ctx, target);
367 if (!bindpt) {
368 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)");
369 return;
370 }
371
372 q = *bindpt;
373
374 switch (pname) {
375 case GL_QUERY_COUNTER_BITS_ARB:
376 *params = 8 * sizeof(q->Result);
377 break;
378 case GL_CURRENT_QUERY_ARB:
379 *params = q ? q->Id : 0;
380 break;
381 default:
382 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
383 return;
384 }
385 }
386
387
388 void GLAPIENTRY
389 _mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
390 {
391 struct gl_query_object *q = NULL;
392 GET_CURRENT_CONTEXT(ctx);
393 ASSERT_OUTSIDE_BEGIN_END(ctx);
394
395 if (MESA_VERBOSE & VERBOSE_API)
396 _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id,
397 _mesa_lookup_enum_by_nr(pname));
398
399 if (id)
400 q = _mesa_lookup_query_object(ctx, id);
401
402 if (!q || q->Active) {
403 _mesa_error(ctx, GL_INVALID_OPERATION,
404 "glGetQueryObjectivARB(id=%d is invalid or active)", id);
405 return;
406 }
407
408 switch (pname) {
409 case GL_QUERY_RESULT_ARB:
410 if (!q->Ready)
411 ctx->Driver.WaitQuery(ctx, q);
412 /* if result is too large for returned type, clamp to max value */
413 if (q->Target == GL_ANY_SAMPLES_PASSED) {
414 if (q->Result)
415 *params = GL_TRUE;
416 else
417 *params = GL_FALSE;
418 } else {
419 if (q->Result > 0x7fffffff) {
420 *params = 0x7fffffff;
421 }
422 else {
423 *params = (GLint)q->Result;
424 }
425 }
426 break;
427 case GL_QUERY_RESULT_AVAILABLE_ARB:
428 if (!q->Ready)
429 ctx->Driver.CheckQuery( ctx, q );
430 *params = q->Ready;
431 break;
432 default:
433 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
434 return;
435 }
436 }
437
438
439 void GLAPIENTRY
440 _mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
441 {
442 struct gl_query_object *q = NULL;
443 GET_CURRENT_CONTEXT(ctx);
444 ASSERT_OUTSIDE_BEGIN_END(ctx);
445
446 if (MESA_VERBOSE & VERBOSE_API)
447 _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id,
448 _mesa_lookup_enum_by_nr(pname));
449
450 if (id)
451 q = _mesa_lookup_query_object(ctx, id);
452
453 if (!q || q->Active) {
454 _mesa_error(ctx, GL_INVALID_OPERATION,
455 "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
456 return;
457 }
458
459 switch (pname) {
460 case GL_QUERY_RESULT_ARB:
461 if (!q->Ready)
462 ctx->Driver.WaitQuery(ctx, q);
463 /* if result is too large for returned type, clamp to max value */
464 if (q->Target == GL_ANY_SAMPLES_PASSED) {
465 if (q->Result)
466 *params = GL_TRUE;
467 else
468 *params = GL_FALSE;
469 } else {
470 if (q->Result > 0xffffffff) {
471 *params = 0xffffffff;
472 }
473 else {
474 *params = (GLuint)q->Result;
475 }
476 }
477 break;
478 case GL_QUERY_RESULT_AVAILABLE_ARB:
479 if (!q->Ready)
480 ctx->Driver.CheckQuery( ctx, q );
481 *params = q->Ready;
482 break;
483 default:
484 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
485 return;
486 }
487 }
488
489
490 /**
491 * New with GL_EXT_timer_query
492 */
493 static void GLAPIENTRY
494 _mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
495 {
496 struct gl_query_object *q = NULL;
497 GET_CURRENT_CONTEXT(ctx);
498 ASSERT_OUTSIDE_BEGIN_END(ctx);
499
500 if (MESA_VERBOSE & VERBOSE_API)
501 _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id,
502 _mesa_lookup_enum_by_nr(pname));
503
504 if (id)
505 q = _mesa_lookup_query_object(ctx, id);
506
507 if (!q || q->Active) {
508 _mesa_error(ctx, GL_INVALID_OPERATION,
509 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
510 return;
511 }
512
513 switch (pname) {
514 case GL_QUERY_RESULT_ARB:
515 if (!q->Ready)
516 ctx->Driver.WaitQuery(ctx, q);
517 *params = q->Result;
518 break;
519 case GL_QUERY_RESULT_AVAILABLE_ARB:
520 if (!q->Ready)
521 ctx->Driver.CheckQuery( ctx, q );
522 *params = q->Ready;
523 break;
524 default:
525 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
526 return;
527 }
528 }
529
530
531 /**
532 * New with GL_EXT_timer_query
533 */
534 static void GLAPIENTRY
535 _mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
536 {
537 struct gl_query_object *q = NULL;
538 GET_CURRENT_CONTEXT(ctx);
539 ASSERT_OUTSIDE_BEGIN_END(ctx);
540
541 if (MESA_VERBOSE & VERBOSE_API)
542 _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id,
543 _mesa_lookup_enum_by_nr(pname));
544
545 if (id)
546 q = _mesa_lookup_query_object(ctx, id);
547
548 if (!q || q->Active) {
549 _mesa_error(ctx, GL_INVALID_OPERATION,
550 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
551 return;
552 }
553
554 switch (pname) {
555 case GL_QUERY_RESULT_ARB:
556 if (!q->Ready)
557 ctx->Driver.WaitQuery(ctx, q);
558 *params = q->Result;
559 break;
560 case GL_QUERY_RESULT_AVAILABLE_ARB:
561 if (!q->Ready)
562 ctx->Driver.CheckQuery( ctx, q );
563 *params = q->Ready;
564 break;
565 default:
566 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
567 return;
568 }
569 }
570
571
572 void
573 _mesa_init_queryobj_dispatch(struct _glapi_table *disp)
574 {
575 SET_GenQueriesARB(disp, _mesa_GenQueriesARB);
576 SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB);
577 SET_IsQueryARB(disp, _mesa_IsQueryARB);
578 SET_BeginQueryARB(disp, _mesa_BeginQueryARB);
579 SET_EndQueryARB(disp, _mesa_EndQueryARB);
580 SET_GetQueryivARB(disp, _mesa_GetQueryivARB);
581 SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB);
582 SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB);
583
584 SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT);
585 SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT);
586 }
587
588
589 #endif /* FEATURE_queryobj */
590
591
592 /**
593 * Allocate/init the context state related to query objects.
594 */
595 void
596 _mesa_init_queryobj(struct gl_context *ctx)
597 {
598 ctx->Query.QueryObjects = _mesa_NewHashTable();
599 ctx->Query.CurrentOcclusionObject = NULL;
600 }
601
602
603 /**
604 * Callback for deleting a query object. Called by _mesa_HashDeleteAll().
605 */
606 static void
607 delete_queryobj_cb(GLuint id, void *data, void *userData)
608 {
609 struct gl_query_object *q= (struct gl_query_object *) data;
610 struct gl_context *ctx = (struct gl_context *)userData;
611 ctx->Driver.DeleteQuery(ctx, q);
612 }
613
614
615 /**
616 * Free the context state related to query objects.
617 */
618 void
619 _mesa_free_queryobj_data(struct gl_context *ctx)
620 {
621 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
622 _mesa_DeleteHashTable(ctx->Query.QueryObjects);
623 }