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