Merge branch 'mesa_7_5_branch'
[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 "hash.h"
29 #include "imports.h"
30 #include "queryobj.h"
31 #include "mtypes.h"
32
33
34 /**
35 * Allocate a new query object. This is a fallback routine called via
36 * ctx->Driver.NewQueryObject().
37 * \param ctx - rendering context
38 * \param id - the new object's ID
39 * \return pointer to new query_object object or NULL if out of memory.
40 */
41 static struct gl_query_object *
42 _mesa_new_query_object(GLcontext *ctx, GLuint id)
43 {
44 struct gl_query_object *q = MALLOC_STRUCT(gl_query_object);
45 (void) ctx;
46 if (q) {
47 q->Id = id;
48 q->Result = 0;
49 q->Active = GL_FALSE;
50 q->Ready = GL_TRUE; /* correct, see spec */
51 }
52 return q;
53 }
54
55
56 /**
57 * Begin a query. Software driver fallback.
58 * Called via ctx->Driver.BeginQuery().
59 */
60 static void
61 _mesa_begin_query(GLcontext *ctx, struct gl_query_object *q)
62 {
63 /* no-op */
64 }
65
66
67 /**
68 * End a query. Software driver fallback.
69 * Called via ctx->Driver.EndQuery().
70 */
71 static void
72 _mesa_end_query(GLcontext *ctx, struct gl_query_object *q)
73 {
74 q->Ready = GL_TRUE;
75 }
76
77
78 /**
79 * Wait for query to complete. Software driver fallback.
80 * Called via ctx->Driver.WaitQuery().
81 */
82 static void
83 _mesa_wait_query(GLcontext *ctx, struct gl_query_object *q)
84 {
85 /* For software drivers, _mesa_end_query() should have completed the query.
86 * For real hardware, implement a proper WaitQuery() driver function,
87 * which may require issuing a flush.
88 */
89 assert(q->Ready);
90 }
91
92
93 /**
94 * Check if a query results are ready. Software driver fallback.
95 * Called via ctx->Driver.CheckQuery().
96 */
97 static void
98 _mesa_check_query(GLcontext *ctx, struct gl_query_object *q)
99 {
100 /* No-op for sw rendering.
101 * HW drivers may need to flush at this time.
102 */
103 }
104
105
106 /**
107 * Delete a query object. Called via ctx->Driver.DeleteQuery().
108 * Not removed from hash table here.
109 */
110 static void
111 _mesa_delete_query(GLcontext *ctx, struct gl_query_object *q)
112 {
113 _mesa_free(q);
114 }
115
116
117 static struct gl_query_object *
118 lookup_query_object(GLcontext *ctx, GLuint id)
119 {
120 return (struct gl_query_object *)
121 _mesa_HashLookup(ctx->Query.QueryObjects, id);
122 }
123
124
125
126 void
127 _mesa_init_query_object_functions(struct dd_function_table *driver)
128 {
129 driver->NewQueryObject = _mesa_new_query_object;
130 driver->DeleteQuery = _mesa_delete_query;
131 driver->BeginQuery = _mesa_begin_query;
132 driver->EndQuery = _mesa_end_query;
133 driver->WaitQuery = _mesa_wait_query;
134 driver->CheckQuery = _mesa_check_query;
135 }
136
137
138 void GLAPIENTRY
139 _mesa_GenQueriesARB(GLsizei n, GLuint *ids)
140 {
141 GLuint first;
142 GET_CURRENT_CONTEXT(ctx);
143 ASSERT_OUTSIDE_BEGIN_END(ctx);
144
145 if (n < 0) {
146 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
147 return;
148 }
149
150 /* No query objects can be active at this time! */
151 if (ctx->Query.CurrentOcclusionObject ||
152 ctx->Query.CurrentTimerObject) {
153 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
154 return;
155 }
156
157 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
158 if (first) {
159 GLsizei i;
160 for (i = 0; i < n; i++) {
161 struct gl_query_object *q
162 = ctx->Driver.NewQueryObject(ctx, first + i);
163 if (!q) {
164 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
165 return;
166 }
167 ids[i] = first + i;
168 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
169 }
170 }
171 }
172
173
174 void GLAPIENTRY
175 _mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
176 {
177 GLint i;
178 GET_CURRENT_CONTEXT(ctx);
179 ASSERT_OUTSIDE_BEGIN_END(ctx);
180
181 if (n < 0) {
182 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
183 return;
184 }
185
186 /* No query objects can be active at this time! */
187 if (ctx->Query.CurrentOcclusionObject ||
188 ctx->Query.CurrentTimerObject) {
189 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
190 return;
191 }
192
193 for (i = 0; i < n; i++) {
194 if (ids[i] > 0) {
195 struct gl_query_object *q = lookup_query_object(ctx, ids[i]);
196 if (q) {
197 ASSERT(!q->Active); /* should be caught earlier */
198 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
199 ctx->Driver.DeleteQuery(ctx, q);
200 }
201 }
202 }
203 }
204
205
206 GLboolean GLAPIENTRY
207 _mesa_IsQueryARB(GLuint id)
208 {
209 GET_CURRENT_CONTEXT(ctx);
210 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
211
212 if (id && lookup_query_object(ctx, id))
213 return GL_TRUE;
214 else
215 return GL_FALSE;
216 }
217
218
219 void GLAPIENTRY
220 _mesa_BeginQueryARB(GLenum target, GLuint id)
221 {
222 struct gl_query_object *q;
223 GET_CURRENT_CONTEXT(ctx);
224 ASSERT_OUTSIDE_BEGIN_END(ctx);
225
226 FLUSH_VERTICES(ctx, _NEW_DEPTH);
227
228 switch (target) {
229 case GL_SAMPLES_PASSED_ARB:
230 if (!ctx->Extensions.ARB_occlusion_query) {
231 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
232 return;
233 }
234 if (ctx->Query.CurrentOcclusionObject) {
235 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
236 return;
237 }
238 break;
239 #if FEATURE_EXT_timer_query
240 case GL_TIME_ELAPSED_EXT:
241 if (!ctx->Extensions.EXT_timer_query) {
242 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
243 return;
244 }
245 if (ctx->Query.CurrentTimerObject) {
246 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB");
247 return;
248 }
249 break;
250 #endif
251 default:
252 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
253 return;
254 }
255
256 if (id == 0) {
257 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
258 return;
259 }
260
261 q = lookup_query_object(ctx, id);
262 if (!q) {
263 /* create new object */
264 q = ctx->Driver.NewQueryObject(ctx, id);
265 if (!q) {
266 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
267 return;
268 }
269 _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
270 }
271 else {
272 /* pre-existing object */
273 if (q->Active) {
274 _mesa_error(ctx, GL_INVALID_OPERATION,
275 "glBeginQueryARB(query already active)");
276 return;
277 }
278 }
279
280 q->Target = target;
281 q->Active = GL_TRUE;
282 q->Result = 0;
283 q->Ready = GL_FALSE;
284
285 if (target == GL_SAMPLES_PASSED_ARB) {
286 ctx->Query.CurrentOcclusionObject = q;
287 }
288 #if FEATURE_EXT_timer_query
289 else if (target == GL_TIME_ELAPSED_EXT) {
290 ctx->Query.CurrentTimerObject = q;
291 }
292 #endif
293
294 ctx->Driver.BeginQuery(ctx, q);
295 }
296
297
298 void GLAPIENTRY
299 _mesa_EndQueryARB(GLenum target)
300 {
301 struct gl_query_object *q;
302 GET_CURRENT_CONTEXT(ctx);
303 ASSERT_OUTSIDE_BEGIN_END(ctx);
304
305 FLUSH_VERTICES(ctx, _NEW_DEPTH);
306
307 switch (target) {
308 case GL_SAMPLES_PASSED_ARB:
309 if (!ctx->Extensions.ARB_occlusion_query) {
310 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
311 return;
312 }
313 q = ctx->Query.CurrentOcclusionObject;
314 ctx->Query.CurrentOcclusionObject = NULL;
315 break;
316 #if FEATURE_EXT_timer_query
317 case GL_TIME_ELAPSED_EXT:
318 if (!ctx->Extensions.EXT_timer_query) {
319 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
320 return;
321 }
322 q = ctx->Query.CurrentTimerObject;
323 ctx->Query.CurrentTimerObject = NULL;
324 break;
325 #endif
326 default:
327 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
328 return;
329 }
330
331 if (!q || !q->Active) {
332 _mesa_error(ctx, GL_INVALID_OPERATION,
333 "glEndQueryARB(no matching glBeginQueryARB)");
334 return;
335 }
336
337 q->Active = GL_FALSE;
338 ctx->Driver.EndQuery(ctx, q);
339 }
340
341
342 void GLAPIENTRY
343 _mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
344 {
345 struct gl_query_object *q;
346 GET_CURRENT_CONTEXT(ctx);
347 ASSERT_OUTSIDE_BEGIN_END(ctx);
348
349 switch (target) {
350 case GL_SAMPLES_PASSED_ARB:
351 if (!ctx->Extensions.ARB_occlusion_query) {
352 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
353 return;
354 }
355 q = ctx->Query.CurrentOcclusionObject;
356 break;
357 #if FEATURE_EXT_timer_query
358 case GL_TIME_ELAPSED_EXT:
359 if (!ctx->Extensions.EXT_timer_query) {
360 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
361 return;
362 }
363 q = ctx->Query.CurrentTimerObject;
364 break;
365 #endif
366 default:
367 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)");
368 return;
369 }
370
371 switch (pname) {
372 case GL_QUERY_COUNTER_BITS_ARB:
373 *params = 8 * sizeof(q->Result);
374 break;
375 case GL_CURRENT_QUERY_ARB:
376 *params = q ? q->Id : 0;
377 break;
378 default:
379 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
380 return;
381 }
382 }
383
384
385 void GLAPIENTRY
386 _mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
387 {
388 struct gl_query_object *q = NULL;
389 GET_CURRENT_CONTEXT(ctx);
390 ASSERT_OUTSIDE_BEGIN_END(ctx);
391
392 if (id)
393 q = lookup_query_object(ctx, id);
394
395 if (!q || q->Active) {
396 _mesa_error(ctx, GL_INVALID_OPERATION,
397 "glGetQueryObjectivARB(id=%d is invalid or active)", id);
398 return;
399 }
400
401 switch (pname) {
402 case GL_QUERY_RESULT_ARB:
403 if (!q->Ready)
404 ctx->Driver.WaitQuery(ctx, q);
405 /* if result is too large for returned type, clamp to max value */
406 if (q->Result > 0x7fffffff) {
407 *params = 0x7fffffff;
408 }
409 else {
410 *params = (GLint)q->Result;
411 }
412 break;
413 case GL_QUERY_RESULT_AVAILABLE_ARB:
414 if (!q->Ready)
415 ctx->Driver.CheckQuery( ctx, q );
416 *params = q->Ready;
417 break;
418 default:
419 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
420 return;
421 }
422 }
423
424
425 void GLAPIENTRY
426 _mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
427 {
428 struct gl_query_object *q = NULL;
429 GET_CURRENT_CONTEXT(ctx);
430 ASSERT_OUTSIDE_BEGIN_END(ctx);
431
432 if (id)
433 q = lookup_query_object(ctx, id);
434
435 if (!q || q->Active) {
436 _mesa_error(ctx, GL_INVALID_OPERATION,
437 "glGetQueryObjectuivARB(id=%d is invalid or active)", id);
438 return;
439 }
440
441 switch (pname) {
442 case GL_QUERY_RESULT_ARB:
443 if (!q->Ready)
444 ctx->Driver.WaitQuery(ctx, q);
445 /* if result is too large for returned type, clamp to max value */
446 if (q->Result > 0xffffffff) {
447 *params = 0xffffffff;
448 }
449 else {
450 *params = (GLuint)q->Result;
451 }
452 break;
453 case GL_QUERY_RESULT_AVAILABLE_ARB:
454 if (!q->Ready)
455 ctx->Driver.CheckQuery( ctx, q );
456 *params = q->Ready;
457 break;
458 default:
459 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
460 return;
461 }
462 }
463
464
465 #if FEATURE_EXT_timer_query
466
467 /**
468 * New with GL_EXT_timer_query
469 */
470 void GLAPIENTRY
471 _mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
472 {
473 struct gl_query_object *q = NULL;
474 GET_CURRENT_CONTEXT(ctx);
475 ASSERT_OUTSIDE_BEGIN_END(ctx);
476
477 if (id)
478 q = lookup_query_object(ctx, id);
479
480 if (!q || q->Active) {
481 _mesa_error(ctx, GL_INVALID_OPERATION,
482 "glGetQueryObjectui64vARB(id=%d is invalid or active)", id);
483 return;
484 }
485
486 switch (pname) {
487 case GL_QUERY_RESULT_ARB:
488 if (!q->Ready)
489 ctx->Driver.WaitQuery(ctx, q);
490 *params = q->Result;
491 break;
492 case GL_QUERY_RESULT_AVAILABLE_ARB:
493 if (!q->Ready)
494 ctx->Driver.CheckQuery( ctx, q );
495 *params = q->Ready;
496 break;
497 default:
498 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)");
499 return;
500 }
501 }
502
503
504 /**
505 * New with GL_EXT_timer_query
506 */
507 void GLAPIENTRY
508 _mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
509 {
510 struct gl_query_object *q = NULL;
511 GET_CURRENT_CONTEXT(ctx);
512 ASSERT_OUTSIDE_BEGIN_END(ctx);
513
514 if (id)
515 q = lookup_query_object(ctx, id);
516
517 if (!q || q->Active) {
518 _mesa_error(ctx, GL_INVALID_OPERATION,
519 "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id);
520 return;
521 }
522
523 switch (pname) {
524 case GL_QUERY_RESULT_ARB:
525 if (!q->Ready)
526 ctx->Driver.WaitQuery(ctx, q);
527 *params = q->Result;
528 break;
529 case GL_QUERY_RESULT_AVAILABLE_ARB:
530 if (!q->Ready)
531 ctx->Driver.CheckQuery( ctx, q );
532 *params = q->Ready;
533 break;
534 default:
535 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)");
536 return;
537 }
538 }
539
540 #endif /* FEATURE_EXT_timer_query */
541
542
543 /**
544 * Allocate/init the context state related to query objects.
545 */
546 void
547 _mesa_init_query(GLcontext *ctx)
548 {
549 #if FEATURE_ARB_occlusion_query
550 ctx->Query.QueryObjects = _mesa_NewHashTable();
551 ctx->Query.CurrentOcclusionObject = NULL;
552 #endif
553 }
554
555
556 /**
557 * Callback for deleting a query object. Called by _mesa_HashDeleteAll().
558 */
559 static void
560 delete_queryobj_cb(GLuint id, void *data, void *userData)
561 {
562 struct gl_query_object *q= (struct gl_query_object *) data;
563 GLcontext *ctx = (GLcontext *)userData;
564 ctx->Driver.DeleteQuery(ctx, q);
565 }
566
567
568 /**
569 * Free the context state related to query objects.
570 */
571 void
572 _mesa_free_query_data(GLcontext *ctx)
573 {
574 _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx);
575 _mesa_DeleteHashTable(ctx->Query.QueryObjects);
576 }