mesa: remove outdated version lines in comments
[mesa.git] / src / mesa / main / feedback.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
5 * Copyright (C) 2009 VMware, Inc. 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 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 /**
27 * \file feedback.c
28 * Selection and feedback modes functions.
29 */
30
31
32 #include "glheader.h"
33 #include "colormac.h"
34 #include "context.h"
35 #include "enums.h"
36 #include "feedback.h"
37 #include "macros.h"
38 #include "mtypes.h"
39 #include "main/dispatch.h"
40
41
42 #define FB_3D 0x01
43 #define FB_4D 0x02
44 #define FB_COLOR 0x04
45 #define FB_TEXTURE 0X08
46
47
48
49 void GLAPIENTRY
50 _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
51 {
52 GET_CURRENT_CONTEXT(ctx);
53
54 if (ctx->RenderMode==GL_FEEDBACK) {
55 _mesa_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
56 return;
57 }
58 if (size<0) {
59 _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
60 return;
61 }
62 if (!buffer && size > 0) {
63 _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
64 ctx->Feedback.BufferSize = 0;
65 return;
66 }
67
68 switch (type) {
69 case GL_2D:
70 ctx->Feedback._Mask = 0;
71 break;
72 case GL_3D:
73 ctx->Feedback._Mask = FB_3D;
74 break;
75 case GL_3D_COLOR:
76 ctx->Feedback._Mask = (FB_3D | FB_COLOR);
77 break;
78 case GL_3D_COLOR_TEXTURE:
79 ctx->Feedback._Mask = (FB_3D | FB_COLOR | FB_TEXTURE);
80 break;
81 case GL_4D_COLOR_TEXTURE:
82 ctx->Feedback._Mask = (FB_3D | FB_4D | FB_COLOR | FB_TEXTURE);
83 break;
84 default:
85 _mesa_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
86 return;
87 }
88
89 FLUSH_VERTICES(ctx, _NEW_RENDERMODE); /* Always flush */
90 ctx->Feedback.Type = type;
91 ctx->Feedback.BufferSize = size;
92 ctx->Feedback.Buffer = buffer;
93 ctx->Feedback.Count = 0; /* Becaues of this. */
94 }
95
96
97 void GLAPIENTRY
98 _mesa_PassThrough( GLfloat token )
99 {
100 GET_CURRENT_CONTEXT(ctx);
101
102 if (ctx->RenderMode==GL_FEEDBACK) {
103 FLUSH_VERTICES(ctx, 0);
104 _mesa_feedback_token( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
105 _mesa_feedback_token( ctx, token );
106 }
107 }
108
109
110 /**
111 * Put a vertex into the feedback buffer.
112 */
113 void
114 _mesa_feedback_vertex(struct gl_context *ctx,
115 const GLfloat win[4],
116 const GLfloat color[4],
117 const GLfloat texcoord[4])
118 {
119 _mesa_feedback_token( ctx, win[0] );
120 _mesa_feedback_token( ctx, win[1] );
121 if (ctx->Feedback._Mask & FB_3D) {
122 _mesa_feedback_token( ctx, win[2] );
123 }
124 if (ctx->Feedback._Mask & FB_4D) {
125 _mesa_feedback_token( ctx, win[3] );
126 }
127 if (ctx->Feedback._Mask & FB_COLOR) {
128 _mesa_feedback_token( ctx, color[0] );
129 _mesa_feedback_token( ctx, color[1] );
130 _mesa_feedback_token( ctx, color[2] );
131 _mesa_feedback_token( ctx, color[3] );
132 }
133 if (ctx->Feedback._Mask & FB_TEXTURE) {
134 _mesa_feedback_token( ctx, texcoord[0] );
135 _mesa_feedback_token( ctx, texcoord[1] );
136 _mesa_feedback_token( ctx, texcoord[2] );
137 _mesa_feedback_token( ctx, texcoord[3] );
138 }
139 }
140
141
142 /**********************************************************************/
143 /** \name Selection */
144 /*@{*/
145
146 /**
147 * Establish a buffer for selection mode values.
148 *
149 * \param size buffer size.
150 * \param buffer buffer.
151 *
152 * \sa glSelectBuffer().
153 *
154 * \note this function can't be put in a display list.
155 *
156 * Verifies we're not in selection mode, flushes the vertices and initialize
157 * the fields in __struct gl_contextRec::Select with the given buffer.
158 */
159 void GLAPIENTRY
160 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
161 {
162 GET_CURRENT_CONTEXT(ctx);
163
164 if (size < 0) {
165 _mesa_error(ctx, GL_INVALID_VALUE, "glSelectBuffer(size)");
166 return;
167 }
168
169 if (ctx->RenderMode==GL_SELECT) {
170 _mesa_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
171 return; /* KW: added return */
172 }
173
174 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
175 ctx->Select.Buffer = buffer;
176 ctx->Select.BufferSize = size;
177 ctx->Select.BufferCount = 0;
178 ctx->Select.HitFlag = GL_FALSE;
179 ctx->Select.HitMinZ = 1.0;
180 ctx->Select.HitMaxZ = 0.0;
181 }
182
183
184 /**
185 * Write a value of a record into the selection buffer.
186 *
187 * \param ctx GL context.
188 * \param value value.
189 *
190 * Verifies there is free space in the buffer to write the value and
191 * increments the pointer.
192 */
193 static inline void
194 write_record(struct gl_context *ctx, GLuint value)
195 {
196 if (ctx->Select.BufferCount < ctx->Select.BufferSize) {
197 ctx->Select.Buffer[ctx->Select.BufferCount] = value;
198 }
199 ctx->Select.BufferCount++;
200 }
201
202
203 /**
204 * Update the hit flag and the maximum and minimum depth values.
205 *
206 * \param ctx GL context.
207 * \param z depth.
208 *
209 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
210 * gl_selection::HitMaxZ.
211 */
212 void
213 _mesa_update_hitflag(struct gl_context *ctx, GLfloat z)
214 {
215 ctx->Select.HitFlag = GL_TRUE;
216 if (z < ctx->Select.HitMinZ) {
217 ctx->Select.HitMinZ = z;
218 }
219 if (z > ctx->Select.HitMaxZ) {
220 ctx->Select.HitMaxZ = z;
221 }
222 }
223
224
225 /**
226 * Write the hit record.
227 *
228 * \param ctx GL context.
229 *
230 * Write the hit record, i.e., the number of names in the stack, the minimum and
231 * maximum depth values and the number of names in the name stack at the time
232 * of the event. Resets the hit flag.
233 *
234 * \sa gl_selection.
235 */
236 static void
237 write_hit_record(struct gl_context *ctx)
238 {
239 GLuint i;
240 GLuint zmin, zmax, zscale = (~0u);
241
242 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
243 /* 2^32-1 and round to nearest unsigned integer. */
244
245 assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
246 zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
247 zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
248
249 write_record( ctx, ctx->Select.NameStackDepth );
250 write_record( ctx, zmin );
251 write_record( ctx, zmax );
252 for (i = 0; i < ctx->Select.NameStackDepth; i++) {
253 write_record( ctx, ctx->Select.NameStack[i] );
254 }
255
256 ctx->Select.Hits++;
257 ctx->Select.HitFlag = GL_FALSE;
258 ctx->Select.HitMinZ = 1.0;
259 ctx->Select.HitMaxZ = -1.0;
260 }
261
262
263 /**
264 * Initialize the name stack.
265 *
266 * Verifies we are in select mode and resets the name stack depth and resets
267 * the hit record data in gl_selection. Marks new render mode in
268 * __struct gl_contextRec::NewState.
269 */
270 void GLAPIENTRY
271 _mesa_InitNames( void )
272 {
273 GET_CURRENT_CONTEXT(ctx);
274 FLUSH_VERTICES(ctx, 0);
275
276 /* Record the hit before the HitFlag is wiped out again. */
277 if (ctx->RenderMode == GL_SELECT) {
278 if (ctx->Select.HitFlag) {
279 write_hit_record( ctx );
280 }
281 }
282 ctx->Select.NameStackDepth = 0;
283 ctx->Select.HitFlag = GL_FALSE;
284 ctx->Select.HitMinZ = 1.0;
285 ctx->Select.HitMaxZ = 0.0;
286 ctx->NewState |= _NEW_RENDERMODE;
287 }
288
289
290 /**
291 * Load the top-most name of the name stack.
292 *
293 * \param name name.
294 *
295 * Verifies we are in selection mode and that the name stack is not empty.
296 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
297 * and replace the top-most name in the stack.
298 *
299 * sa __struct gl_contextRec::Select.
300 */
301 void GLAPIENTRY
302 _mesa_LoadName( GLuint name )
303 {
304 GET_CURRENT_CONTEXT(ctx);
305
306 if (ctx->RenderMode != GL_SELECT) {
307 return;
308 }
309 if (ctx->Select.NameStackDepth == 0) {
310 _mesa_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
311 return;
312 }
313
314 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
315
316 if (ctx->Select.HitFlag) {
317 write_hit_record( ctx );
318 }
319 if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
320 ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
321 }
322 else {
323 ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
324 }
325 }
326
327
328 /**
329 * Push a name into the name stack.
330 *
331 * \param name name.
332 *
333 * Verifies we are in selection mode and that the name stack is not full.
334 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
335 * and adds the name to the top of the name stack.
336 *
337 * sa __struct gl_contextRec::Select.
338 */
339 void GLAPIENTRY
340 _mesa_PushName( GLuint name )
341 {
342 GET_CURRENT_CONTEXT(ctx);
343
344 if (ctx->RenderMode != GL_SELECT) {
345 return;
346 }
347
348 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
349 if (ctx->Select.HitFlag) {
350 write_hit_record( ctx );
351 }
352 if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) {
353 _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
354 }
355 else
356 ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
357 }
358
359
360 /**
361 * Pop a name into the name stack.
362 *
363 * Verifies we are in selection mode and that the name stack is not empty.
364 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
365 * and removes top-most name in the name stack.
366 *
367 * sa __struct gl_contextRec::Select.
368 */
369 void GLAPIENTRY
370 _mesa_PopName( void )
371 {
372 GET_CURRENT_CONTEXT(ctx);
373
374 if (ctx->RenderMode != GL_SELECT) {
375 return;
376 }
377
378 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
379 if (ctx->Select.HitFlag) {
380 write_hit_record( ctx );
381 }
382 if (ctx->Select.NameStackDepth == 0) {
383 _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
384 }
385 else
386 ctx->Select.NameStackDepth--;
387 }
388
389 /*@}*/
390
391
392 /**********************************************************************/
393 /** \name Render Mode */
394 /*@{*/
395
396 /**
397 * Set rasterization mode.
398 *
399 * \param mode rasterization mode.
400 *
401 * \note this function can't be put in a display list.
402 *
403 * \sa glRenderMode().
404 *
405 * Flushes the vertices and do the necessary cleanup according to the previous
406 * rasterization mode, such as writing the hit record or resent the select
407 * buffer index when exiting the select mode. Updates
408 * __struct gl_contextRec::RenderMode and notifies the driver via the
409 * dd_function_table::RenderMode callback.
410 */
411 GLint GLAPIENTRY
412 _mesa_RenderMode( GLenum mode )
413 {
414 GET_CURRENT_CONTEXT(ctx);
415 GLint result;
416 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
417
418 if (MESA_VERBOSE & VERBOSE_API)
419 _mesa_debug(ctx, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode));
420
421 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
422
423 switch (ctx->RenderMode) {
424 case GL_RENDER:
425 result = 0;
426 break;
427 case GL_SELECT:
428 if (ctx->Select.HitFlag) {
429 write_hit_record( ctx );
430 }
431 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
432 /* overflow */
433 #ifdef DEBUG
434 _mesa_warning(ctx, "Feedback buffer overflow");
435 #endif
436 result = -1;
437 }
438 else {
439 result = ctx->Select.Hits;
440 }
441 ctx->Select.BufferCount = 0;
442 ctx->Select.Hits = 0;
443 ctx->Select.NameStackDepth = 0;
444 break;
445 case GL_FEEDBACK:
446 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
447 /* overflow */
448 result = -1;
449 }
450 else {
451 result = ctx->Feedback.Count;
452 }
453 ctx->Feedback.Count = 0;
454 break;
455 default:
456 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
457 return 0;
458 }
459
460 switch (mode) {
461 case GL_RENDER:
462 break;
463 case GL_SELECT:
464 if (ctx->Select.BufferSize==0) {
465 /* haven't called glSelectBuffer yet */
466 _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
467 }
468 break;
469 case GL_FEEDBACK:
470 if (ctx->Feedback.BufferSize==0) {
471 /* haven't called glFeedbackBuffer yet */
472 _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
473 }
474 break;
475 default:
476 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
477 return 0;
478 }
479
480 ctx->RenderMode = mode;
481 if (ctx->Driver.RenderMode)
482 ctx->Driver.RenderMode( ctx, mode );
483
484 return result;
485 }
486
487 /*@}*/
488
489
490 /**********************************************************************/
491 /** \name Initialization */
492 /*@{*/
493
494 /**
495 * Initialize context feedback data.
496 */
497 void _mesa_init_feedback( struct gl_context * ctx )
498 {
499 /* Feedback */
500 ctx->Feedback.Type = GL_2D; /* TODO: verify */
501 ctx->Feedback.Buffer = NULL;
502 ctx->Feedback.BufferSize = 0;
503 ctx->Feedback.Count = 0;
504
505 /* Selection/picking */
506 ctx->Select.Buffer = NULL;
507 ctx->Select.BufferSize = 0;
508 ctx->Select.BufferCount = 0;
509 ctx->Select.Hits = 0;
510 ctx->Select.NameStackDepth = 0;
511
512 /* Miscellaneous */
513 ctx->RenderMode = GL_RENDER;
514 }
515
516 /*@}*/