Home | History | Annotate | Line # | Download | only in common
      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 
     31 typedef 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 
     39 static void
     40 analyze_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 
     58 static nir_ssa_def *
     59 cull_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_build_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_build_load_cull_front_face_enabled_amd(b);
     76    nir_ssa_def *cull_back = nir_build_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 
     88 static nir_ssa_def *
     89 cull_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_build_load_viewport_x_scale(b), nir_build_load_viewport_y_scale(b), };
    104       nir_ssa_def *vp_translate[2] = { nir_build_load_viewport_x_offset(b), nir_build_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_build_load_cull_small_primitives_enabled_amd(b));
    118       {
    119          nir_ssa_def *small_prim_precision = nir_build_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 
    150 nir_ssa_def *
    151 ac_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