1/**************************************************************************
2 *
3 * Copyright 2007 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * \brief  Drawing stage for user culling
30 */
31
32#include "util/u_math.h"
33#include "util/u_memory.h"
34#include "pipe/p_defines.h"
35#include "draw_pipe.h"
36
37struct user_cull_stage {
38   struct draw_stage stage;
39};
40
41static inline struct user_cull_stage *user_cull_stage( struct draw_stage *stage )
42{
43   return (struct user_cull_stage *)stage;
44}
45
46static inline boolean
47cull_distance_is_out(float dist)
48{
49   return (dist < 0.0f) || util_is_inf_or_nan(dist);
50}
51
52/*
53 * If the shader writes the culldistance then we can
54 * perform distance based culling. Distance based
55 * culling doesn't require a face and can be performed
56 * on primitives without faces (e.g. points and lines)
57 */
58static void user_cull_point( struct draw_stage *stage,
59                        struct prim_header *header )
60{
61   const unsigned num_written_culldistances =
62      draw_current_shader_num_written_culldistances(stage->draw);
63   const unsigned num_written_clipdistances =
64      draw_current_shader_num_written_clipdistances(stage->draw);
65   unsigned i;
66
67   debug_assert(num_written_culldistances);
68
69   for (i = 0; i < num_written_culldistances; ++i) {
70      unsigned cull_idx = (num_written_clipdistances + i) / 4;
71      unsigned out_idx =
72         draw_current_shader_ccdistance_output(stage->draw, cull_idx);
73      unsigned idx = (num_written_clipdistances + i) % 4;
74      float cull1 = header->v[0]->data[out_idx][idx];
75      boolean vert1_out = cull_distance_is_out(cull1);
76      if (vert1_out)
77         return;
78   }
79   stage->next->point( stage->next, header );
80}
81
82/*
83 * If the shader writes the culldistance then we can
84 * perform distance based culling. Distance based
85 * culling doesn't require a face and can be performed
86 * on primitives without faces (e.g. points and lines)
87 */
88static void user_cull_line( struct draw_stage *stage,
89                       struct prim_header *header )
90{
91   const unsigned num_written_culldistances =
92      draw_current_shader_num_written_culldistances(stage->draw);
93   const unsigned num_written_clipdistances =
94      draw_current_shader_num_written_clipdistances(stage->draw);
95   unsigned i;
96
97   debug_assert(num_written_culldistances);
98
99   for (i = 0; i < num_written_culldistances; ++i) {
100      unsigned cull_idx = (num_written_clipdistances + i) / 4;
101      unsigned out_idx =
102         draw_current_shader_ccdistance_output(stage->draw, cull_idx);
103      unsigned idx = (num_written_clipdistances + i) % 4;
104      float cull1 = header->v[0]->data[out_idx][idx];
105      float cull2 = header->v[1]->data[out_idx][idx];
106      boolean vert1_out = cull_distance_is_out(cull1);
107      boolean vert2_out = cull_distance_is_out(cull2);
108      if (vert1_out && vert2_out)
109         return;
110   }
111   stage->next->line( stage->next, header );
112}
113
114/*
115 * Triangles can be culled either using the cull distance
116 * shader outputs or the regular face culling. If required
117 * this function performs both, starting with distance culling.
118 */
119static void user_cull_tri( struct draw_stage *stage,
120                           struct prim_header *header )
121{
122   const unsigned num_written_culldistances =
123      draw_current_shader_num_written_culldistances(stage->draw);
124   const unsigned num_written_clipdistances =
125      draw_current_shader_num_written_clipdistances(stage->draw);
126   unsigned i;
127
128   debug_assert(num_written_culldistances);
129
130   /* Do the distance culling */
131   for (i = 0; i < num_written_culldistances; ++i) {
132      unsigned cull_idx = (num_written_clipdistances + i) / 4;
133      unsigned out_idx =
134         draw_current_shader_ccdistance_output(stage->draw, cull_idx);
135      unsigned idx = (num_written_clipdistances + i) % 4;
136      float cull1 = header->v[0]->data[out_idx][idx];
137      float cull2 = header->v[1]->data[out_idx][idx];
138      float cull3 = header->v[2]->data[out_idx][idx];
139      boolean vert1_out = cull_distance_is_out(cull1);
140      boolean vert2_out = cull_distance_is_out(cull2);
141      boolean vert3_out = cull_distance_is_out(cull3);
142      if (vert1_out && vert2_out && vert3_out) {
143         return;
144      }
145   }
146   stage->next->tri( stage->next, header );
147}
148
149static void user_cull_flush( struct draw_stage *stage, unsigned flags )
150{
151   stage->point = user_cull_point;
152   stage->line = user_cull_line;
153   stage->tri = user_cull_tri;
154   stage->next->flush( stage->next, flags );
155}
156
157static void user_cull_reset_stipple_counter( struct draw_stage *stage )
158{
159   stage->next->reset_stipple_counter( stage->next );
160}
161
162static void user_cull_destroy( struct draw_stage *stage )
163{
164   draw_free_temp_verts( stage );
165   FREE( stage );
166}
167
168/**
169 * Create a new polygon culling stage.
170 */
171struct draw_stage *draw_user_cull_stage( struct draw_context *draw )
172{
173   struct user_cull_stage *user_cull = CALLOC_STRUCT(user_cull_stage);
174   if (!user_cull)
175      goto fail;
176
177   user_cull->stage.draw = draw;
178   user_cull->stage.name = "user_cull";
179   user_cull->stage.next = NULL;
180   user_cull->stage.point = user_cull_point;
181   user_cull->stage.line = user_cull_line;
182   user_cull->stage.tri = user_cull_tri;
183   user_cull->stage.flush = user_cull_flush;
184   user_cull->stage.reset_stipple_counter = user_cull_reset_stipple_counter;
185   user_cull->stage.destroy = user_cull_destroy;
186
187   if (!draw_alloc_temp_verts( &user_cull->stage, 0 ))
188      goto fail;
189
190   return &user_cull->stage;
191
192fail:
193   if (user_cull)
194      user_cull->stage.destroy( &user_cull->stage );
195
196   return NULL;
197}
198