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 "c99_alloca.h"
34 #include "context.h"
35 #include "enums.h"
36 #include "feedback.h"
37 #include "macros.h"
38 #include "mtypes.h"
39 #include "api_exec_decl.h"
40 #include "bufferobj.h"
41
42 #include "state_tracker/st_cb_feedback.h"
43
44 #define FB_3D 0x01
45 #define FB_4D 0x02
46 #define FB_COLOR 0x04
47 #define FB_TEXTURE 0X08
48
49
50
51 void GLAPIENTRY
_mesa_FeedbackBuffer(GLsizei size,GLenum type,GLfloat * buffer)52 _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
53 {
54 GET_CURRENT_CONTEXT(ctx);
55
56 if (ctx->RenderMode==GL_FEEDBACK) {
57 _mesa_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
58 return;
59 }
60 if (size<0) {
61 _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
62 return;
63 }
64 if (!buffer && size > 0) {
65 _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
66 ctx->Feedback.BufferSize = 0;
67 return;
68 }
69
70 switch (type) {
71 case GL_2D:
72 ctx->Feedback._Mask = 0;
73 break;
74 case GL_3D:
75 ctx->Feedback._Mask = FB_3D;
76 break;
77 case GL_3D_COLOR:
78 ctx->Feedback._Mask = (FB_3D | FB_COLOR);
79 break;
80 case GL_3D_COLOR_TEXTURE:
81 ctx->Feedback._Mask = (FB_3D | FB_COLOR | FB_TEXTURE);
82 break;
83 case GL_4D_COLOR_TEXTURE:
84 ctx->Feedback._Mask = (FB_3D | FB_4D | FB_COLOR | FB_TEXTURE);
85 break;
86 default:
87 _mesa_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
88 return;
89 }
90
91 FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0); /* Always flush */
92 ctx->Feedback.Type = type;
93 ctx->Feedback.BufferSize = size;
94 ctx->Feedback.Buffer = buffer;
95 ctx->Feedback.Count = 0; /* Because of this. */
96 }
97
98
99 void GLAPIENTRY
_mesa_PassThrough(GLfloat token)100 _mesa_PassThrough( GLfloat token )
101 {
102 GET_CURRENT_CONTEXT(ctx);
103
104 if (ctx->RenderMode==GL_FEEDBACK) {
105 FLUSH_VERTICES(ctx, 0, 0);
106 _mesa_feedback_token( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
107 _mesa_feedback_token( ctx, token );
108 }
109 }
110
111
112 /**
113 * Put a vertex into the feedback buffer.
114 */
115 void
_mesa_feedback_vertex(struct gl_context * ctx,const GLfloat win[4],const GLfloat color[4],const GLfloat texcoord[4])116 _mesa_feedback_vertex(struct gl_context *ctx,
117 const GLfloat win[4],
118 const GLfloat color[4],
119 const GLfloat texcoord[4])
120 {
121 _mesa_feedback_token( ctx, win[0] );
122 _mesa_feedback_token( ctx, win[1] );
123 if (ctx->Feedback._Mask & FB_3D) {
124 _mesa_feedback_token( ctx, win[2] );
125 }
126 if (ctx->Feedback._Mask & FB_4D) {
127 _mesa_feedback_token( ctx, win[3] );
128 }
129 if (ctx->Feedback._Mask & FB_COLOR) {
130 _mesa_feedback_token( ctx, color[0] );
131 _mesa_feedback_token( ctx, color[1] );
132 _mesa_feedback_token( ctx, color[2] );
133 _mesa_feedback_token( ctx, color[3] );
134 }
135 if (ctx->Feedback._Mask & FB_TEXTURE) {
136 _mesa_feedback_token( ctx, texcoord[0] );
137 _mesa_feedback_token( ctx, texcoord[1] );
138 _mesa_feedback_token( ctx, texcoord[2] );
139 _mesa_feedback_token( ctx, texcoord[3] );
140 }
141 }
142
143
144 /**********************************************************************/
145 /** \name Selection */
146 /*@{*/
147
148 /**
149 * Establish a buffer for selection mode values.
150 *
151 * \param size buffer size.
152 * \param buffer buffer.
153 *
154 * \sa glSelectBuffer().
155 *
156 * \note this function can't be put in a display list.
157 *
158 * Verifies we're not in selection mode, flushes the vertices and initialize
159 * the fields in __struct gl_contextRec::Select with the given buffer.
160 */
161 void GLAPIENTRY
_mesa_SelectBuffer(GLsizei size,GLuint * buffer)162 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
163 {
164 GET_CURRENT_CONTEXT(ctx);
165
166 if (size < 0) {
167 _mesa_error(ctx, GL_INVALID_VALUE, "glSelectBuffer(size)");
168 return;
169 }
170
171 if (ctx->RenderMode==GL_SELECT) {
172 _mesa_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
173 return; /* KW: added return */
174 }
175
176 FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0);
177 ctx->Select.Buffer = buffer;
178 ctx->Select.BufferSize = size;
179 ctx->Select.BufferCount = 0;
180 ctx->Select.HitFlag = GL_FALSE;
181 ctx->Select.HitMinZ = 1.0;
182 ctx->Select.HitMaxZ = 0.0;
183 }
184
185
186 /**
187 * Write a value of a record into the selection buffer.
188 *
189 * \param ctx GL context.
190 * \param value value.
191 *
192 * Verifies there is free space in the buffer to write the value and
193 * increments the pointer.
194 */
195 static inline void
write_record(struct gl_context * ctx,GLuint value)196 write_record(struct gl_context *ctx, GLuint value)
197 {
198 if (ctx->Select.BufferCount < ctx->Select.BufferSize) {
199 ctx->Select.Buffer[ctx->Select.BufferCount] = value;
200 }
201 ctx->Select.BufferCount++;
202 }
203
204
205 /**
206 * Update the hit flag and the maximum and minimum depth values.
207 *
208 * \param ctx GL context.
209 * \param z depth.
210 *
211 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
212 * gl_selection::HitMaxZ.
213 */
214 void
_mesa_update_hitflag(struct gl_context * ctx,GLfloat z)215 _mesa_update_hitflag(struct gl_context *ctx, GLfloat z)
216 {
217 ctx->Select.HitFlag = GL_TRUE;
218 if (z < ctx->Select.HitMinZ) {
219 ctx->Select.HitMinZ = z;
220 }
221 if (z > ctx->Select.HitMaxZ) {
222 ctx->Select.HitMaxZ = z;
223 }
224 }
225
226 static void
alloc_select_resource(struct gl_context * ctx)227 alloc_select_resource(struct gl_context *ctx)
228 {
229 struct gl_selection *s = &ctx->Select;
230
231 if (!ctx->Const.HardwareAcceleratedSelect)
232 return;
233
234 if (!ctx->HWSelectModeBeginEnd) {
235 ctx->HWSelectModeBeginEnd = _mesa_alloc_dispatch_table(false);
236 if (!ctx->HWSelectModeBeginEnd) {
237 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Cannot allocate HWSelectModeBeginEnd");
238 return;
239 }
240 vbo_install_hw_select_begin_end(ctx);
241 }
242
243 if (!s->SaveBuffer) {
244 s->SaveBuffer = malloc(NAME_STACK_BUFFER_SIZE);
245 if (!s->SaveBuffer) {
246 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Cannot allocate name stack save buffer");
247 return;
248 }
249 }
250
251 if (!s->Result) {
252 s->Result = _mesa_bufferobj_alloc(ctx, -1);
253 if (!s->Result) {
254 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Cannot allocate select result buffer");
255 return;
256 }
257
258 GLuint init_result[MAX_NAME_STACK_RESULT_NUM * 3];
259 for (int i = 0; i < MAX_NAME_STACK_RESULT_NUM; i++) {
260 init_result[i * 3] = 0; /* hit */
261 init_result[i * 3 + 1] = 0xffffffff; /* minz */
262 init_result[i * 3 + 2] = 0; /* maxz */
263 }
264
265 bool success = _mesa_bufferobj_data(ctx,
266 GL_SHADER_STORAGE_BUFFER,
267 sizeof(init_result),
268 init_result,
269 GL_STATIC_DRAW, 0,
270 s->Result);
271 if (!success) {
272 _mesa_reference_buffer_object(ctx, &s->Result, NULL);
273 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Cannot init result buffer");
274 return;
275 }
276 }
277 }
278
279 static bool
save_used_name_stack(struct gl_context * ctx)280 save_used_name_stack(struct gl_context *ctx)
281 {
282 struct gl_selection *s = &ctx->Select;
283
284 if (!ctx->Const.HardwareAcceleratedSelect)
285 return false;
286
287 /* We have two kinds of name stack user:
288 * 1. glRasterPos (CPU based) will set HitFlag
289 * 2. draw call for GPU will set ResultUsed
290 */
291 if (!s->ResultUsed && !s->HitFlag)
292 return false;
293
294 void *save = (char *)s->SaveBuffer + s->SaveBufferTail;
295
296 /* save meta data */
297 uint8_t *metadata = save;
298 metadata[0] = s->HitFlag;
299 metadata[1] = s->ResultUsed;
300 metadata[2] = s->NameStackDepth;
301 metadata[3] = 0;
302
303 /* save hit data */
304 int index = 1;
305 if (s->HitFlag) {
306 float *hit = save;
307 hit[index++] = s->HitMinZ;
308 hit[index++] = s->HitMaxZ;
309 }
310
311 /* save name stack */
312 memcpy((uint32_t *)save + index, s->NameStack, s->NameStackDepth * sizeof(GLuint));
313 index += s->NameStackDepth;
314
315 s->SaveBufferTail += index * sizeof(GLuint);
316 s->SavedStackNum++;
317
318 /* if current slot has been used, store result to next slot in result buffer */
319 if (s->ResultUsed)
320 s->ResultOffset += 3 * sizeof(GLuint);
321
322 /* reset fields */
323 s->HitFlag = GL_FALSE;
324 s->HitMinZ = 1.0;
325 s->HitMaxZ = 0;
326
327 s->ResultUsed = GL_FALSE;
328
329 /* return true if we have no enough space for the next name stack data */
330 return s->ResultOffset >= MAX_NAME_STACK_RESULT_NUM * 3 * sizeof(GLuint) ||
331 s->SaveBufferTail >= NAME_STACK_BUFFER_SIZE - (MAX_NAME_STACK_DEPTH + 3) * sizeof(GLuint);
332 }
333
334 static void
update_hit_record(struct gl_context * ctx)335 update_hit_record(struct gl_context *ctx)
336 {
337 struct gl_selection *s = &ctx->Select;
338
339 if (ctx->Const.HardwareAcceleratedSelect) {
340 if (!s->SavedStackNum)
341 return;
342
343 unsigned size = s->ResultOffset;
344 GLuint *result = size ? alloca(size) : NULL;
345 _mesa_bufferobj_get_subdata(ctx, 0, size, result, s->Result);
346
347 unsigned index = 0;
348 unsigned *save = s->SaveBuffer;
349 for (int i = 0; i < s->SavedStackNum; i++) {
350 uint8_t *metadata = (uint8_t *)(save++);
351
352 unsigned zmin, zmax;
353 bool cpu_hit = !!metadata[0];
354 if (cpu_hit) {
355 /* map [0, 1] to [0, UINT_MAX]*/
356 zmin = (unsigned) ((float)(~0u) * *(float *)(save++));
357 zmax = (unsigned) ((float)(~0u) * *(float *)(save++));
358 } else {
359 zmin = ~0u;
360 zmax = 0;
361 }
362
363 bool gpu_hit = false;
364 if (metadata[1]) {
365 gpu_hit = !!result[index];
366
367 if (gpu_hit) {
368 zmin = MIN2(zmin, result[index + 1]);
369 zmax = MAX2(zmax, result[index + 2]);
370
371 /* reset data */
372 result[index] = 0; /* hit */
373 result[index + 1] = 0xffffffff; /* minz */
374 result[index + 2] = 0; /* maxz */
375 }
376 index += 3;
377 }
378
379 int depth = metadata[2];
380 if (cpu_hit || gpu_hit) {
381 /* hit */
382 write_record(ctx, depth);
383 write_record(ctx, zmin);
384 write_record(ctx, zmax);
385
386 for (int j = 0; j < depth; j++)
387 write_record(ctx, save[j]);
388 s->Hits++;
389 }
390 save += depth;
391 }
392
393 /* reset result buffer */
394 _mesa_bufferobj_subdata(ctx, 0, size, result, s->Result);
395
396 s->SaveBufferTail = 0;
397 s->SavedStackNum = 0;
398 s->ResultOffset = 0;
399 } else {
400 if (!s->HitFlag)
401 return;
402
403 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
404 /* 2^32-1 and round to nearest unsigned integer. */
405 GLuint zscale = (~0u);
406 GLuint zmin = (GLuint) ((GLfloat) zscale * s->HitMinZ);
407 GLuint zmax = (GLuint) ((GLfloat) zscale * s->HitMaxZ);
408
409 write_record(ctx, s->NameStackDepth);
410 write_record(ctx, zmin);
411 write_record(ctx, zmax);
412 for (int i = 0; i < s->NameStackDepth; i++)
413 write_record(ctx, s->NameStack[i]);
414
415 s->HitFlag = GL_FALSE;
416 s->HitMinZ = 1.0;
417 s->HitMaxZ = -1.0;
418
419 s->Hits++;
420 }
421 }
422
423 static void
reset_name_stack_to_empty(struct gl_context * ctx)424 reset_name_stack_to_empty(struct gl_context *ctx)
425 {
426 struct gl_selection *s = &ctx->Select;
427
428 s->NameStackDepth = 0;
429 s->HitFlag = GL_FALSE;
430 s->HitMinZ = 1.0;
431 s->HitMaxZ = 0.0;
432
433 if (ctx->Const.HardwareAcceleratedSelect) {
434 s->SaveBufferTail = 0;
435 s->SavedStackNum = 0;
436 s->ResultUsed = GL_FALSE;
437 s->ResultOffset = 0;
438 }
439 }
440
441 /**
442 * Initialize the name stack.
443 */
444 void GLAPIENTRY
_mesa_InitNames(void)445 _mesa_InitNames( void )
446 {
447 GET_CURRENT_CONTEXT(ctx);
448
449 /* Calls to glInitNames while the render mode is not GL_SELECT are ignored. */
450 if (ctx->RenderMode != GL_SELECT)
451 return;
452
453 FLUSH_VERTICES(ctx, 0, 0);
454
455 save_used_name_stack(ctx);
456 update_hit_record(ctx);
457
458 reset_name_stack_to_empty(ctx);
459
460 ctx->NewState |= _NEW_RENDERMODE;
461 }
462
463
464 /**
465 * Load the top-most name of the name stack.
466 *
467 * \param name name.
468 */
469 void GLAPIENTRY
_mesa_LoadName(GLuint name)470 _mesa_LoadName( GLuint name )
471 {
472 GET_CURRENT_CONTEXT(ctx);
473
474 if (ctx->RenderMode != GL_SELECT) {
475 return;
476 }
477 if (ctx->Select.NameStackDepth == 0) {
478 _mesa_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
479 return;
480 }
481
482 if (!ctx->Const.HardwareAcceleratedSelect || save_used_name_stack(ctx)) {
483 FLUSH_VERTICES(ctx, 0, 0);
484 update_hit_record(ctx);
485 }
486
487 ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
488 ctx->NewState |= _NEW_RENDERMODE;
489 }
490
491
492 /**
493 * Push a name into the name stack.
494 *
495 * \param name name.
496 */
497 void GLAPIENTRY
_mesa_PushName(GLuint name)498 _mesa_PushName( GLuint name )
499 {
500 GET_CURRENT_CONTEXT(ctx);
501
502 if (ctx->RenderMode != GL_SELECT) {
503 return;
504 }
505
506 if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) {
507 _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
508 return;
509 }
510
511 if (!ctx->Const.HardwareAcceleratedSelect || save_used_name_stack(ctx)) {
512 FLUSH_VERTICES(ctx, 0, 0);
513 update_hit_record(ctx);
514 }
515
516 ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
517 ctx->NewState |= _NEW_RENDERMODE;
518 }
519
520
521 /**
522 * Pop a name into the name stack.
523 */
524 void GLAPIENTRY
_mesa_PopName(void)525 _mesa_PopName( void )
526 {
527 GET_CURRENT_CONTEXT(ctx);
528
529 if (ctx->RenderMode != GL_SELECT) {
530 return;
531 }
532
533 if (ctx->Select.NameStackDepth == 0) {
534 _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
535 return;
536 }
537
538 if (!ctx->Const.HardwareAcceleratedSelect || save_used_name_stack(ctx)) {
539 FLUSH_VERTICES(ctx, 0, 0);
540 update_hit_record(ctx);
541 }
542
543 ctx->Select.NameStackDepth--;
544 ctx->NewState |= _NEW_RENDERMODE;
545 }
546
547 /*@}*/
548
549
550 /**********************************************************************/
551 /** \name Render Mode */
552 /*@{*/
553
554 /**
555 * Set rasterization mode.
556 *
557 * \param mode rasterization mode.
558 *
559 * \note this function can't be put in a display list.
560 *
561 * \sa glRenderMode().
562 *
563 * Flushes the vertices and do the necessary cleanup according to the previous
564 * rasterization mode, such as writing the hit record or resent the select
565 * buffer index when exiting the select mode. Updates
566 * __struct gl_contextRec::RenderMode and notifies the driver via the
567 * dd_function_table::RenderMode callback.
568 */
569 GLint GLAPIENTRY
_mesa_RenderMode(GLenum mode)570 _mesa_RenderMode( GLenum mode )
571 {
572 GET_CURRENT_CONTEXT(ctx);
573 GLint result;
574 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
575
576 if (MESA_VERBOSE & VERBOSE_API)
577 _mesa_debug(ctx, "glRenderMode %s\n", _mesa_enum_to_string(mode));
578
579 FLUSH_VERTICES(ctx, _NEW_RENDERMODE | _NEW_FF_VERT_PROGRAM |
580 _NEW_FF_FRAG_PROGRAM, 0);
581
582 switch (ctx->RenderMode) {
583 case GL_RENDER:
584 result = 0;
585 break;
586 case GL_SELECT:
587 save_used_name_stack(ctx);
588 update_hit_record(ctx);
589
590 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
591 /* overflow */
592 #ifndef NDEBUG
593 _mesa_warning(ctx, "Feedback buffer overflow");
594 #endif
595 result = -1;
596 }
597 else {
598 result = ctx->Select.Hits;
599 }
600 ctx->Select.BufferCount = 0;
601 ctx->Select.Hits = 0;
602 /* name stack should be in empty state after exiting GL_SELECT mode */
603 reset_name_stack_to_empty(ctx);
604 break;
605 case GL_FEEDBACK:
606 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
607 /* overflow */
608 result = -1;
609 }
610 else {
611 result = ctx->Feedback.Count;
612 }
613 ctx->Feedback.Count = 0;
614 break;
615 default:
616 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
617 return 0;
618 }
619
620 switch (mode) {
621 case GL_RENDER:
622 break;
623 case GL_SELECT:
624 if (ctx->Select.BufferSize==0) {
625 /* haven't called glSelectBuffer yet */
626 _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
627 }
628 alloc_select_resource(ctx);
629 break;
630 case GL_FEEDBACK:
631 if (ctx->Feedback.BufferSize==0) {
632 /* haven't called glFeedbackBuffer yet */
633 _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
634 }
635 break;
636 default:
637 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
638 return 0;
639 }
640
641 st_RenderMode( ctx, mode );
642
643 /* finally update render mode to new one */
644 ctx->RenderMode = mode;
645
646 return result;
647 }
648
649 /*@}*/
650
651
652 /**********************************************************************/
653 /** \name Initialization */
654 /*@{*/
655
656 /**
657 * Initialize context feedback data.
658 */
_mesa_init_feedback(struct gl_context * ctx)659 void _mesa_init_feedback( struct gl_context * ctx )
660 {
661 /* Feedback */
662 ctx->Feedback.Type = GL_2D; /* TODO: verify */
663 ctx->Feedback.Buffer = NULL;
664 ctx->Feedback.BufferSize = 0;
665 ctx->Feedback.Count = 0;
666
667 /* Selection/picking */
668 ctx->Select.Buffer = NULL;
669 ctx->Select.BufferSize = 0;
670 ctx->Select.BufferCount = 0;
671 ctx->Select.Hits = 0;
672 ctx->Select.NameStackDepth = 0;
673
674 /* Miscellaneous */
675 ctx->RenderMode = GL_RENDER;
676 }
677
_mesa_free_feedback(struct gl_context * ctx)678 void _mesa_free_feedback(struct gl_context * ctx)
679 {
680 struct gl_selection *s = &ctx->Select;
681
682 free(s->SaveBuffer);
683 _mesa_reference_buffer_object(ctx, &s->Result, NULL);
684 }
685
686 /*@}*/
687