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 polygon culling 30 */ 31 32 /* Authors: Keith Whitwell <keithw (at) vmware.com> 33 */ 34 35 36 #include "util/u_math.h" 37 #include "util/u_memory.h" 38 #include "pipe/p_defines.h" 39 #include "draw_pipe.h" 40 41 42 struct cull_stage { 43 struct draw_stage stage; 44 unsigned cull_face; /**< which face(s) to cull (one of PIPE_FACE_x) */ 45 unsigned front_ccw; 46 }; 47 48 49 static inline struct cull_stage *cull_stage( struct draw_stage *stage ) 50 { 51 return (struct cull_stage *)stage; 52 } 53 54 static inline boolean 55 cull_distance_is_out(float dist) 56 { 57 return (dist < 0.0f) || util_is_inf_or_nan(dist); 58 } 59 60 /* 61 * If the shader writes the culldistance then we can 62 * perform distance based culling. Distance based 63 * culling doesn't require a face and can be performed 64 * on primitives without faces (e.g. points and lines) 65 */ 66 static void cull_point( struct draw_stage *stage, 67 struct prim_header *header ) 68 { 69 const unsigned num_written_culldistances = 70 draw_current_shader_num_written_culldistances(stage->draw); 71 const unsigned num_written_clipdistances = 72 draw_current_shader_num_written_clipdistances(stage->draw); 73 unsigned i; 74 75 debug_assert(num_written_culldistances); 76 77 for (i = 0; i < num_written_culldistances; ++i) { 78 unsigned cull_idx = (num_written_clipdistances + i) / 4; 79 unsigned out_idx = 80 draw_current_shader_ccdistance_output(stage->draw, cull_idx); 81 unsigned idx = (num_written_clipdistances + i) % 4; 82 float cull1 = header->v[0]->data[out_idx][idx]; 83 boolean vert1_out = cull_distance_is_out(cull1); 84 if (vert1_out) 85 return; 86 } 87 stage->next->point( stage->next, header ); 88 } 89 90 /* 91 * If the shader writes the culldistance then we can 92 * perform distance based culling. Distance based 93 * culling doesn't require a face and can be performed 94 * on primitives without faces (e.g. points and lines) 95 */ 96 static void cull_line( struct draw_stage *stage, 97 struct prim_header *header ) 98 { 99 const unsigned num_written_culldistances = 100 draw_current_shader_num_written_culldistances(stage->draw); 101 const unsigned num_written_clipdistances = 102 draw_current_shader_num_written_clipdistances(stage->draw); 103 unsigned i; 104 105 debug_assert(num_written_culldistances); 106 107 for (i = 0; i < num_written_culldistances; ++i) { 108 unsigned cull_idx = (num_written_clipdistances + i) / 4; 109 unsigned out_idx = 110 draw_current_shader_ccdistance_output(stage->draw, cull_idx); 111 unsigned idx = (num_written_clipdistances + i) % 4; 112 float cull1 = header->v[0]->data[out_idx][idx]; 113 float cull2 = header->v[1]->data[out_idx][idx]; 114 boolean vert1_out = cull_distance_is_out(cull1); 115 boolean vert2_out = cull_distance_is_out(cull2); 116 if (vert1_out && vert2_out) 117 return; 118 } 119 stage->next->line( stage->next, header ); 120 } 121 122 /* 123 * Triangles can be culled either using the cull distance 124 * shader outputs or the regular face culling. If required 125 * this function performs both, starting with distance culling. 126 */ 127 static void cull_tri( struct draw_stage *stage, 128 struct prim_header *header ) 129 { 130 const unsigned num_written_culldistances = 131 draw_current_shader_num_written_culldistances(stage->draw); 132 const unsigned num_written_clipdistances = 133 draw_current_shader_num_written_clipdistances(stage->draw); 134 /* Do the distance culling */ 135 if (num_written_culldistances) { 136 unsigned i; 137 for (i = 0; i < num_written_culldistances; ++i) { 138 unsigned cull_idx = (num_written_clipdistances + i) / 4; 139 unsigned out_idx = 140 draw_current_shader_ccdistance_output(stage->draw, cull_idx); 141 unsigned idx = (num_written_clipdistances + i) % 4; 142 float cull1 = header->v[0]->data[out_idx][idx]; 143 float cull2 = header->v[1]->data[out_idx][idx]; 144 float cull3 = header->v[2]->data[out_idx][idx]; 145 boolean vert1_out = cull_distance_is_out(cull1); 146 boolean vert2_out = cull_distance_is_out(cull2); 147 boolean vert3_out = cull_distance_is_out(cull3); 148 if (vert1_out && vert2_out && vert3_out) 149 return; 150 } 151 } 152 153 /* Do the regular face culling */ 154 { 155 const unsigned pos = draw_current_shader_position_output(stage->draw); 156 /* Window coords: */ 157 const float *v0 = header->v[0]->data[pos]; 158 const float *v1 = header->v[1]->data[pos]; 159 const float *v2 = header->v[2]->data[pos]; 160 161 /* edge vectors: e = v0 - v2, f = v1 - v2 */ 162 const float ex = v0[0] - v2[0]; 163 const float ey = v0[1] - v2[1]; 164 const float fx = v1[0] - v2[0]; 165 const float fy = v1[1] - v2[1]; 166 167 168 /* det = cross(e,f).z */ 169 header->det = ex * fy - ey * fx; 170 171 if (header->det != 0) { 172 /* if det < 0 then Z points toward the camera and the triangle is 173 * counter-clockwise winding. 174 */ 175 unsigned ccw = (header->det < 0); 176 unsigned face = ((ccw == cull_stage(stage)->front_ccw) ? 177 PIPE_FACE_FRONT : 178 PIPE_FACE_BACK); 179 180 if ((face & cull_stage(stage)->cull_face) == 0) { 181 /* triangle is not culled, pass to next stage */ 182 stage->next->tri( stage->next, header ); 183 } 184 } else { 185 /* 186 * With zero area, this is back facing (because the spec says 187 * it's front facing if sign is positive?). 188 * Some apis apparently do not allow us to cull zero area tris 189 * here, in case of fill mode line (which is rather lame). 190 */ 191 if ((PIPE_FACE_BACK & cull_stage(stage)->cull_face) == 0) { 192 stage->next->tri( stage->next, header ); 193 } 194 } 195 } 196 } 197 198 static void cull_first_point( struct draw_stage *stage, 199 struct prim_header *header ) 200 { 201 const unsigned num_written_culldistances = 202 draw_current_shader_num_written_culldistances(stage->draw); 203 204 if (num_written_culldistances) { 205 stage->point = cull_point; 206 stage->point( stage, header ); 207 } else { 208 stage->point = draw_pipe_passthrough_point; 209 stage->point( stage, header ); 210 } 211 } 212 213 static void cull_first_line( struct draw_stage *stage, 214 struct prim_header *header ) 215 { 216 const unsigned num_written_culldistances = 217 draw_current_shader_num_written_culldistances(stage->draw); 218 219 if (num_written_culldistances) { 220 stage->line = cull_line; 221 stage->line( stage, header ); 222 } else { 223 stage->line = draw_pipe_passthrough_line; 224 stage->line( stage, header ); 225 } 226 } 227 228 static void cull_first_tri( struct draw_stage *stage, 229 struct prim_header *header ) 230 { 231 struct cull_stage *cull = cull_stage(stage); 232 233 cull->cull_face = stage->draw->rasterizer->cull_face; 234 cull->front_ccw = stage->draw->rasterizer->front_ccw; 235 236 stage->tri = cull_tri; 237 stage->tri( stage, header ); 238 } 239 240 241 static void cull_flush( struct draw_stage *stage, unsigned flags ) 242 { 243 stage->point = cull_first_point; 244 stage->line = cull_first_line; 245 stage->tri = cull_first_tri; 246 stage->next->flush( stage->next, flags ); 247 } 248 249 250 static void cull_reset_stipple_counter( struct draw_stage *stage ) 251 { 252 stage->next->reset_stipple_counter( stage->next ); 253 } 254 255 256 static void cull_destroy( struct draw_stage *stage ) 257 { 258 draw_free_temp_verts( stage ); 259 FREE( stage ); 260 } 261 262 263 /** 264 * Create a new polygon culling stage. 265 */ 266 struct draw_stage *draw_cull_stage( struct draw_context *draw ) 267 { 268 struct cull_stage *cull = CALLOC_STRUCT(cull_stage); 269 if (!cull) 270 goto fail; 271 272 cull->stage.draw = draw; 273 cull->stage.name = "cull"; 274 cull->stage.next = NULL; 275 cull->stage.point = cull_first_point; 276 cull->stage.line = cull_first_line; 277 cull->stage.tri = cull_first_tri; 278 cull->stage.flush = cull_flush; 279 cull->stage.reset_stipple_counter = cull_reset_stipple_counter; 280 cull->stage.destroy = cull_destroy; 281 282 if (!draw_alloc_temp_verts( &cull->stage, 0 )) 283 goto fail; 284 285 return &cull->stage; 286 287 fail: 288 if (cull) 289 cull->stage.destroy( &cull->stage ); 290 291 return NULL; 292 } 293