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