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