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