Rearrange the code related to GL_ARB_occlusion_object to generalize query
[mesa.git] / src / mesa / main / occlude.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5
4 *
5 * Copyright (C) 1999-2005 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 /*
27 * Functions to implement the GL_ARB_occlusion_query extension.
28 */
29
30
31 #include "glheader.h"
32 #include "context.h"
33 #include "hash.h"
34 #include "imports.h"
35 #include "occlude.h"
36 #include "mtypes.h"
37
38
39 /**
40 * Allocate a new occlusion query object.
41 * \param target - must be GL_SAMPLES_PASSED_ARB at this time
42 * \param id - the 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 new_query_object(GLenum target, GLuint id)
47 {
48 struct gl_query_object *q = MALLOC_STRUCT(gl_query_object);
49 if (q) {
50 q->Target = target;
51 q->Id = id;
52 q->Result = 0;
53 q->Active = GL_FALSE;
54 }
55 return q;
56 }
57
58
59 /**
60 * Delete an occlusion query object.
61 * Not removed from hash table here.
62 */
63 static void
64 delete_query_object(struct gl_query_object *q)
65 {
66 FREE(q);
67 }
68
69
70 struct gl_query_object *
71 lookup_query_object(GLcontext *ctx, GLuint id)
72 {
73 return (struct gl_query_object *)
74 _mesa_HashLookup(ctx->Query.QueryObjects, id);
75 }
76
77
78
79 void GLAPIENTRY
80 _mesa_GenQueriesARB(GLsizei n, GLuint *ids)
81 {
82 GET_CURRENT_CONTEXT(ctx);
83 GLuint first;
84 ASSERT_OUTSIDE_BEGIN_END(ctx);
85
86 if (n < 0) {
87 _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)");
88 return;
89 }
90
91 /* No query objects can be active at this time! */
92 if (ctx->Query.CurrentOcclusionObject) {
93 _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB");
94 return;
95 }
96
97 first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n);
98 if (first) {
99 GLsizei i;
100 for (i = 0; i < n; i++) {
101 struct gl_query_object *q = new_query_object(GL_SAMPLES_PASSED_ARB,
102 first + i);
103 if (!q) {
104 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB");
105 return;
106 }
107 ids[i] = first + i;
108 _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q);
109 }
110 }
111 }
112
113
114 void GLAPIENTRY
115 _mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids)
116 {
117 GET_CURRENT_CONTEXT(ctx);
118 GLint i;
119 ASSERT_OUTSIDE_BEGIN_END(ctx);
120
121 if (n < 0) {
122 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)");
123 return;
124 }
125
126 /* No query objects can be active at this time! */
127 if (ctx->Query.CurrentOcclusionObject) {
128 _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB");
129 return;
130 }
131
132 for (i = 0; i < n; i++) {
133 if (ids[i] > 0) {
134 struct gl_query_object *q = lookup_query_object(ctx, ids[i]);
135 if (q) {
136 ASSERT(!q->Active); /* should be caught earlier */
137 _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]);
138 delete_query_object(q);
139 }
140 }
141 }
142 }
143
144
145 GLboolean GLAPIENTRY
146 _mesa_IsQueryARB(GLuint id)
147 {
148 GET_CURRENT_CONTEXT(ctx);
149 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
150
151 if (id && lookup_query_object(ctx, id))
152 return GL_TRUE;
153 else
154 return GL_FALSE;
155 }
156
157
158 void GLAPIENTRY
159 _mesa_BeginQueryARB(GLenum target, GLuint id)
160 {
161 GET_CURRENT_CONTEXT(ctx);
162 struct gl_query_object *q;
163 ASSERT_OUTSIDE_BEGIN_END(ctx);
164
165 FLUSH_VERTICES(ctx, _NEW_DEPTH);
166
167 if (target != GL_SAMPLES_PASSED_ARB) {
168 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)");
169 return;
170 }
171
172 if (id == 0) {
173 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)");
174 return;
175 }
176
177 if (ctx->Query.CurrentOcclusionObject) {
178 _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(target)");
179 return;
180 }
181
182 q = lookup_query_object(ctx, id);
183 if (!q) {
184 /* create new object */
185 q = new_query_object(target, id);
186 if (!q) {
187 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB");
188 return;
189 }
190 _mesa_HashInsert(ctx->Query.QueryObjects, id, q);
191 }
192 else {
193 /* pre-existing object */
194 if (q->Target != target) {
195 _mesa_error(ctx, GL_INVALID_OPERATION,
196 "glBeginQueryARB(target mismatch)");
197 return;
198 }
199 if (q->Active) {
200 _mesa_error(ctx, GL_INVALID_OPERATION,
201 "glBeginQueryARB(query already active)");
202 return;
203 }
204 }
205
206 q->Active = GL_TRUE;
207 q->Result = 0;
208 q->Ready = GL_FALSE;
209 ctx->Query.CurrentOcclusionObject = q;
210
211 if (ctx->Driver.BeginQuery) {
212 ctx->Driver.BeginQuery(ctx, q);
213 }
214 }
215
216
217 void GLAPIENTRY
218 _mesa_EndQueryARB(GLenum target)
219 {
220 GET_CURRENT_CONTEXT(ctx);
221 struct gl_query_object *q;
222 ASSERT_OUTSIDE_BEGIN_END(ctx);
223
224 FLUSH_VERTICES(ctx, _NEW_DEPTH);
225
226 switch (target) {
227 case GL_SAMPLES_PASSED_ARB:
228 q = ctx->Query.CurrentOcclusionObject;
229 ctx->Query.CurrentOcclusionObject = NULL;
230 break;
231 default:
232 _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)");
233 return;
234 }
235
236 if (!q || !q->Active) {
237 _mesa_error(ctx, GL_INVALID_OPERATION,
238 "glEndQueryARB(no matching glBeginQueryARB)");
239 return;
240 }
241
242 q->Active = GL_FALSE;
243 if (ctx->Driver.EndQuery) {
244 ctx->Driver.EndQuery(ctx, q);
245 }
246 else {
247 q->Ready = GL_TRUE;
248 }
249 }
250
251
252 void GLAPIENTRY
253 _mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params)
254 {
255 GET_CURRENT_CONTEXT(ctx);
256 struct gl_query_object *q;
257 ASSERT_OUTSIDE_BEGIN_END(ctx);
258
259 switch (target) {
260 case GL_SAMPLES_PASSED_ARB:
261 q = ctx->Query.CurrentOcclusionObject;
262 break;
263 default:
264 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(target)");
265 return;
266 }
267
268 switch (pname) {
269 case GL_QUERY_COUNTER_BITS_ARB:
270 *params = 8 * sizeof(q->Result);
271 break;
272 case GL_CURRENT_QUERY_ARB:
273 *params = q ? q->Id : 0;
274 break;
275 default:
276 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)");
277 return;
278 }
279 }
280
281
282 void GLAPIENTRY
283 _mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params)
284 {
285 GET_CURRENT_CONTEXT(ctx);
286 struct gl_query_object *q = NULL;
287 ASSERT_OUTSIDE_BEGIN_END(ctx);
288
289 if (id)
290 q = lookup_query_object(ctx, id);
291
292 if (!q || q->Active) {
293 _mesa_error(ctx, GL_INVALID_OPERATION,
294 "glGetQueryObjectivARB(id=%d is active)", id);
295 return;
296 }
297
298 switch (pname) {
299 case GL_QUERY_RESULT_ARB:
300 *params = q->Result;
301 break;
302 case GL_QUERY_RESULT_AVAILABLE_ARB:
303 /* XXX revisit when we have a hardware implementation! */
304 *params = q->Ready;
305 break;
306 default:
307 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)");
308 return;
309 }
310 }
311
312
313 void GLAPIENTRY
314 _mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params)
315 {
316 GET_CURRENT_CONTEXT(ctx);
317 struct gl_query_object *q = NULL;
318 ASSERT_OUTSIDE_BEGIN_END(ctx);
319
320 if (id)
321 q = lookup_query_object(ctx, id);
322
323 if (!q || q->Active) {
324 _mesa_error(ctx, GL_INVALID_OPERATION,
325 "glGetQueryObjectuivARB(id=%d is active)", id);
326 return;
327 }
328
329 switch (pname) {
330 case GL_QUERY_RESULT_ARB:
331 *params = q->Result;
332 break;
333 case GL_QUERY_RESULT_AVAILABLE_ARB:
334 /* XXX revisit when we have a hardware implementation! */
335 *params = q->Ready;
336 break;
337 default:
338 _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)");
339 return;
340 }
341 }
342
343
344
345 /**
346 * Allocate/init the context state related to occlusion query objects.
347 */
348 void
349 _mesa_init_occlude(GLcontext *ctx)
350 {
351 #if FEATURE_ARB_occlusion_query
352 ctx->Query.QueryObjects = _mesa_NewHashTable();
353 ctx->Query.CurrentOcclusionObject = NULL;
354 #endif
355 }
356
357
358 /**
359 * Free the context state related to occlusion query objects.
360 */
361 void
362 _mesa_free_occlude_data(GLcontext *ctx)
363 {
364 while (1) {
365 GLuint id = _mesa_HashFirstEntry(ctx->Query.QueryObjects);
366 if (id) {
367 struct gl_query_object *q = lookup_query_object(ctx, id);
368 ASSERT(q);
369 delete_query_object(q);
370 _mesa_HashRemove(ctx->Query.QueryObjects, id);
371 }
372 else {
373 break;
374 }
375 }
376 _mesa_DeleteHashTable(ctx->Query.QueryObjects);
377 }