1/* 2 * Copyright 2019 Advanced Micro Devices, Inc. 3 * Copyright 2021 Valve Corporation 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 * 24 */ 25 26#include "ac_nir.h" 27#include "nir_builder.h" 28 29/* This code is adapted from ac_llvm_cull.c, hence the copyright to AMD. */ 30 31typedef struct 32{ 33 nir_ssa_def *w_reflection; 34 nir_ssa_def *w_accepted; 35 nir_ssa_def *all_w_positive; 36 nir_ssa_def *any_w_negative; 37} position_w_info; 38 39static void 40analyze_position_w(nir_builder *b, nir_ssa_def *pos[3][4], position_w_info *w_info) 41{ 42 nir_ssa_def *all_w_negative = nir_imm_bool(b, true); 43 44 w_info->w_reflection = nir_imm_bool(b, false); 45 w_info->any_w_negative = nir_imm_bool(b, false); 46 47 for (unsigned i = 0; i < 3; ++i) { 48 nir_ssa_def *neg_w = nir_flt(b, pos[i][3], nir_imm_float(b, 0.0f)); 49 w_info->w_reflection = nir_ixor(b, neg_w, w_info->w_reflection); 50 w_info->any_w_negative = nir_ior(b, neg_w, w_info->any_w_negative); 51 all_w_negative = nir_iand(b, neg_w, all_w_negative); 52 } 53 54 w_info->all_w_positive = nir_inot(b, w_info->any_w_negative); 55 w_info->w_accepted = nir_inot(b, all_w_negative); 56} 57 58static nir_ssa_def * 59cull_face(nir_builder *b, nir_ssa_def *pos[3][4], const position_w_info *w_info) 60{ 61 nir_ssa_def *det_t0 = nir_fsub(b, pos[2][0], pos[0][0]); 62 nir_ssa_def *det_t1 = nir_fsub(b, pos[1][1], pos[0][1]); 63 nir_ssa_def *det_t2 = nir_fsub(b, pos[0][0], pos[1][0]); 64 nir_ssa_def *det_t3 = nir_fsub(b, pos[0][1], pos[2][1]); 65 nir_ssa_def *det_p0 = nir_fmul(b, det_t0, det_t1); 66 nir_ssa_def *det_p1 = nir_fmul(b, det_t2, det_t3); 67 nir_ssa_def *det = nir_fsub(b, det_p0, det_p1); 68 69 det = nir_bcsel(b, w_info->w_reflection, nir_fneg(b, det), det); 70 71 nir_ssa_def *front_facing_cw = nir_flt(b, det, nir_imm_float(b, 0.0f)); 72 nir_ssa_def *front_facing_ccw = nir_flt(b, nir_imm_float(b, 0.0f), det); 73 nir_ssa_def *ccw = nir_load_cull_ccw_amd(b); 74 nir_ssa_def *front_facing = nir_bcsel(b, ccw, front_facing_ccw, front_facing_cw); 75 nir_ssa_def *cull_front = nir_load_cull_front_face_enabled_amd(b); 76 nir_ssa_def *cull_back = nir_load_cull_back_face_enabled_amd(b); 77 78 nir_ssa_def *face_culled = nir_bcsel(b, front_facing, cull_front, cull_back); 79 80 /* Don't reject NaN and +/-infinity, these are tricky. 81 * Just trust fixed-function HW to handle these cases correctly. 82 */ 83 face_culled = nir_iand(b, face_culled, nir_fisfinite(b, det)); 84 85 return nir_inot(b, face_culled); 86} 87 88static nir_ssa_def * 89cull_bbox(nir_builder *b, nir_ssa_def *pos[3][4], nir_ssa_def *accepted, const position_w_info *w_info) 90{ 91 nir_ssa_def *bbox_accepted = NULL; 92 nir_ssa_def *try_cull_bbox = nir_iand(b, accepted, w_info->all_w_positive); 93 94 nir_if *if_cull_bbox = nir_push_if(b, try_cull_bbox); 95 { 96 nir_ssa_def *bbox_min[3] = {0}, *bbox_max[3] = {0}; 97 98 for (unsigned chan = 0; chan < 2; ++chan) { 99 bbox_min[chan] = nir_fmin(b, pos[0][chan], nir_fmin(b, pos[1][chan], pos[2][chan])); 100 bbox_max[chan] = nir_fmax(b, pos[0][chan], nir_fmax(b, pos[1][chan], pos[2][chan])); 101 } 102 103 nir_ssa_def *vp_scale[2] = { nir_load_viewport_x_scale(b), nir_load_viewport_y_scale(b), }; 104 nir_ssa_def *vp_translate[2] = { nir_load_viewport_x_offset(b), nir_load_viewport_y_offset(b), }; 105 nir_ssa_def *prim_outside_view = nir_imm_false(b); 106 107 /* Frustrum culling - eliminate triangles that are fully outside the view. */ 108 for (unsigned chan = 0; chan < 2; ++chan) { 109 prim_outside_view = nir_ior(b, prim_outside_view, nir_flt(b, bbox_max[chan], nir_imm_float(b, -1.0f))); 110 prim_outside_view = nir_ior(b, prim_outside_view, nir_flt(b, nir_imm_float(b, 1.0f), bbox_min[chan])); 111 } 112 113 nir_ssa_def *prim_is_small = NULL; 114 nir_ssa_def *prim_is_small_else = nir_imm_false(b); 115 116 /* Small primitive filter - eliminate triangles that are too small to affect a sample. */ 117 nir_if *if_cull_small_prims = nir_push_if(b, nir_load_cull_small_primitives_enabled_amd(b)); 118 { 119 nir_ssa_def *small_prim_precision = nir_load_cull_small_prim_precision_amd(b); 120 prim_is_small = nir_imm_false(b); 121 122 for (unsigned chan = 0; chan < 2; ++chan) { 123 /* Convert the position to screen-space coordinates. */ 124 nir_ssa_def *min = nir_ffma(b, bbox_min[chan], vp_scale[chan], vp_translate[chan]); 125 nir_ssa_def *max = nir_ffma(b, bbox_max[chan], vp_scale[chan], vp_translate[chan]); 126 127 /* Scale the bounding box according to precision. */ 128 min = nir_fsub(b, min, small_prim_precision); 129 max = nir_fadd(b, max, small_prim_precision); 130 131 /* Determine if the bbox intersects the sample point, by checking if the min and max round to the same int. */ 132 min = nir_fround_even(b, min); 133 max = nir_fround_even(b, max); 134 135 nir_ssa_def *rounded_to_eq = nir_feq(b, min, max); 136 prim_is_small = nir_ior(b, prim_is_small, rounded_to_eq); 137 } 138 } 139 nir_pop_if(b, if_cull_small_prims); 140 141 prim_is_small = nir_if_phi(b, prim_is_small, prim_is_small_else); 142 nir_ssa_def *prim_invisible = nir_ior(b, prim_outside_view, prim_is_small); 143 144 bbox_accepted = nir_inot(b, prim_invisible); 145 } 146 nir_pop_if(b, if_cull_bbox); 147 return nir_if_phi(b, bbox_accepted, accepted); 148} 149 150nir_ssa_def * 151ac_nir_cull_triangle(nir_builder *b, 152 nir_ssa_def *initially_accepted, 153 nir_ssa_def *pos[3][4]) 154{ 155 position_w_info w_info = {0}; 156 analyze_position_w(b, pos, &w_info); 157 158 nir_ssa_def *accepted = initially_accepted; 159 accepted = nir_iand(b, accepted, w_info.w_accepted); 160 accepted = nir_iand(b, accepted, cull_face(b, pos, &w_info)); 161 accepted = nir_iand(b, accepted, cull_bbox(b, pos, accepted, &w_info)); 162 163 return accepted; 164} 165