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