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