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
37 struct user_cull_stage {
38 struct draw_stage stage;
39 };
40
41 static inline boolean
cull_distance_is_out(float dist)42 cull_distance_is_out(float dist)
43 {
44 return (dist < 0.0f) || util_is_inf_or_nan(dist);
45 }
46
47 /*
48 * If the shader writes the culldistance then we can
49 * perform distance based culling. Distance based
50 * culling doesn't require a face and can be performed
51 * on primitives without faces (e.g. points and lines)
52 */
user_cull_point(struct draw_stage * stage,struct prim_header * header)53 static void user_cull_point( struct draw_stage *stage,
54 struct prim_header *header )
55 {
56 const unsigned num_written_culldistances =
57 draw_current_shader_num_written_culldistances(stage->draw);
58 const unsigned num_written_clipdistances =
59 draw_current_shader_num_written_clipdistances(stage->draw);
60 unsigned i;
61
62 assert(num_written_culldistances);
63
64 for (i = 0; i < num_written_culldistances; ++i) {
65 unsigned cull_idx = (num_written_clipdistances + i) / 4;
66 unsigned out_idx =
67 draw_current_shader_ccdistance_output(stage->draw, cull_idx);
68 unsigned idx = (num_written_clipdistances + i) % 4;
69 float cull1 = header->v[0]->data[out_idx][idx];
70 boolean vert1_out = cull_distance_is_out(cull1);
71 if (vert1_out)
72 return;
73 }
74 stage->next->point( stage->next, header );
75 }
76
77 /*
78 * If the shader writes the culldistance then we can
79 * perform distance based culling. Distance based
80 * culling doesn't require a face and can be performed
81 * on primitives without faces (e.g. points and lines)
82 */
user_cull_line(struct draw_stage * stage,struct prim_header * header)83 static void user_cull_line( struct draw_stage *stage,
84 struct prim_header *header )
85 {
86 const unsigned num_written_culldistances =
87 draw_current_shader_num_written_culldistances(stage->draw);
88 const unsigned num_written_clipdistances =
89 draw_current_shader_num_written_clipdistances(stage->draw);
90 unsigned i;
91
92 assert(num_written_culldistances);
93
94 for (i = 0; i < num_written_culldistances; ++i) {
95 unsigned cull_idx = (num_written_clipdistances + i) / 4;
96 unsigned out_idx =
97 draw_current_shader_ccdistance_output(stage->draw, cull_idx);
98 unsigned idx = (num_written_clipdistances + i) % 4;
99 float cull1 = header->v[0]->data[out_idx][idx];
100 float cull2 = header->v[1]->data[out_idx][idx];
101 boolean vert1_out = cull_distance_is_out(cull1);
102 boolean vert2_out = cull_distance_is_out(cull2);
103 if (vert1_out && vert2_out)
104 return;
105 }
106 stage->next->line( stage->next, header );
107 }
108
109 /*
110 * Triangles can be culled either using the cull distance
111 * shader outputs or the regular face culling. If required
112 * this function performs both, starting with distance culling.
113 */
user_cull_tri(struct draw_stage * stage,struct prim_header * header)114 static void user_cull_tri( struct draw_stage *stage,
115 struct prim_header *header )
116 {
117 const unsigned num_written_culldistances =
118 draw_current_shader_num_written_culldistances(stage->draw);
119 const unsigned num_written_clipdistances =
120 draw_current_shader_num_written_clipdistances(stage->draw);
121 unsigned i;
122
123 assert(num_written_culldistances);
124
125 /* Do the distance culling */
126 for (i = 0; i < num_written_culldistances; ++i) {
127 unsigned cull_idx = (num_written_clipdistances + i) / 4;
128 unsigned out_idx =
129 draw_current_shader_ccdistance_output(stage->draw, cull_idx);
130 unsigned idx = (num_written_clipdistances + i) % 4;
131 float cull1 = header->v[0]->data[out_idx][idx];
132 float cull2 = header->v[1]->data[out_idx][idx];
133 float cull3 = header->v[2]->data[out_idx][idx];
134 boolean vert1_out = cull_distance_is_out(cull1);
135 boolean vert2_out = cull_distance_is_out(cull2);
136 boolean vert3_out = cull_distance_is_out(cull3);
137 if (vert1_out && vert2_out && vert3_out) {
138 return;
139 }
140 }
141 stage->next->tri( stage->next, header );
142 }
143
user_cull_flush(struct draw_stage * stage,unsigned flags)144 static void user_cull_flush( struct draw_stage *stage, unsigned flags )
145 {
146 stage->point = user_cull_point;
147 stage->line = user_cull_line;
148 stage->tri = user_cull_tri;
149 stage->next->flush( stage->next, flags );
150 }
151
user_cull_reset_stipple_counter(struct draw_stage * stage)152 static void user_cull_reset_stipple_counter( struct draw_stage *stage )
153 {
154 stage->next->reset_stipple_counter( stage->next );
155 }
156
user_cull_destroy(struct draw_stage * stage)157 static void user_cull_destroy( struct draw_stage *stage )
158 {
159 draw_free_temp_verts( stage );
160 FREE( stage );
161 }
162
163 /**
164 * Create a new polygon culling stage.
165 */
draw_user_cull_stage(struct draw_context * draw)166 struct draw_stage *draw_user_cull_stage( struct draw_context *draw )
167 {
168 struct user_cull_stage *user_cull = CALLOC_STRUCT(user_cull_stage);
169 if (!user_cull)
170 goto fail;
171
172 user_cull->stage.draw = draw;
173 user_cull->stage.name = "user_cull";
174 user_cull->stage.next = NULL;
175 user_cull->stage.point = user_cull_point;
176 user_cull->stage.line = user_cull_line;
177 user_cull->stage.tri = user_cull_tri;
178 user_cull->stage.flush = user_cull_flush;
179 user_cull->stage.reset_stipple_counter = user_cull_reset_stipple_counter;
180 user_cull->stage.destroy = user_cull_destroy;
181
182 if (!draw_alloc_temp_verts( &user_cull->stage, 0 ))
183 goto fail;
184
185 return &user_cull->stage;
186
187 fail:
188 if (user_cull)
189 user_cull->stage.destroy( &user_cull->stage );
190
191 return NULL;
192 }
193