1/* 2 * Copyright © 2020 Mike Blumenkrantz 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 * 23 * Authors: 24 * Mike Blumenkrantz <michael.blumenkrantz@gmail.com> 25 */ 26 27#include "nir.h" 28#include "nir_builder.h" 29 30 31/** 32 * This pass uses the enabled clip planes from the rasterizer state to rewrite 33 * vertex shader store operations and store a 0 to the corresponding gl_ClipDistance[n] 34 * value if the plane is disabled 35 */ 36 37/* recursively nest if/else blocks until we get to an array index, 38 * then overwrite it if that plane isn't enabled 39 */ 40static void 41recursive_if_chain(nir_builder *b, nir_deref_instr *deref, nir_ssa_def *value, unsigned clip_plane_enable, nir_ssa_def *index, unsigned start, unsigned end) 42{ 43 if (start == end - 1) { 44 /* store the original value again if the clip plane is enabled */ 45 if (clip_plane_enable & (1 << start)) 46 nir_store_deref(b, deref, value, 1 << start); 47 else 48 nir_store_deref(b, deref, nir_imm_int(b, 0), 1 << start); 49 return; 50 } 51 52 unsigned mid = start + (end - start) / 2; 53 nir_push_if(b, nir_ilt(b, index, nir_imm_int(b, mid))); 54 recursive_if_chain(b, deref, value, clip_plane_enable, index, start, mid); 55 nir_push_else(b, NULL); 56 recursive_if_chain(b, deref, value, clip_plane_enable, index, mid, end); 57 nir_pop_if(b, NULL); 58} 59 60/* vulkan (and some drivers) provides no concept of enabling clip planes through api, 61 * so we rewrite disabled clip planes to a zero value in order to disable them 62 */ 63static bool 64lower_clip_plane_store(nir_intrinsic_instr *instr, unsigned clip_plane_enable, nir_builder *b) 65{ 66 nir_variable *out; 67 unsigned plane; 68 69 if (instr->intrinsic != nir_intrinsic_store_deref) 70 return false; 71 72 nir_deref_instr *deref = nir_src_as_deref(instr->src[0]); 73 74 out = nir_deref_instr_get_variable(deref); 75 if ((out->data.location != VARYING_SLOT_CLIP_DIST0 && 76 out->data.location != VARYING_SLOT_CLIP_DIST1) || 77 out->data.mode != nir_var_shader_out) 78 return false; 79 80 b->cursor = nir_after_instr(&instr->instr); 81 if (deref->deref_type == nir_deref_type_var) { 82 int wrmask = nir_intrinsic_write_mask(instr); 83 84 nir_ssa_def *components[4]; 85 int start = out->data.location == VARYING_SLOT_CLIP_DIST1 ? 4 : 0; 86 /* rewrite components as zeroes for planes that aren't enabled */ 87 for (int i = 0; i < 4; i++) { 88 if (wrmask & (1 << i)) { 89 if (!(clip_plane_enable & (1 << (start + i)))) 90 components[i] = nir_imm_int(b, 0); 91 else 92 components[i] = nir_channel(b, nir_ssa_for_src(b, instr->src[1], nir_src_num_components(instr->src[1])), i); 93 } else 94 components[i] = nir_ssa_undef(b, 1, 32); 95 } 96 nir_store_deref(b, deref, nir_vec(b, components, instr->num_components), wrmask); 97 } else if (nir_src_is_const(deref->arr.index)) { 98 /* storing using a constant index */ 99 plane = nir_src_as_uint(deref->arr.index); 100 /* no need to make changes if the clip plane is enabled */ 101 if (clip_plane_enable & (1 << plane)) 102 return false; 103 104 assert(nir_intrinsic_write_mask(instr) == 1); 105 nir_store_deref(b, deref, nir_imm_int(b, 0), 1); 106 } else { 107 /* storing using a variable index */ 108 nir_ssa_def *index = nir_ssa_for_src(b, deref->arr.index, 1); 109 unsigned length = glsl_get_length(nir_deref_instr_parent(deref)->type); 110 111 recursive_if_chain(b, deref, instr->src[1].ssa, clip_plane_enable, index, 0, length); 112 } 113 nir_instr_remove(&instr->instr); 114 return true; 115} 116 117bool 118nir_lower_clip_disable(nir_shader *shader, unsigned clip_plane_enable) 119{ 120 bool progress = false; 121 122 /* if all user planes are enabled in API that are written in the array, always ignore; 123 * this explicitly covers the 2x vec4 case 124 */ 125 if (clip_plane_enable == u_bit_consecutive(0, shader->info.clip_distance_array_size)) 126 return false; 127 128 nir_foreach_function(function, shader) { 129 if (function->impl) { 130 nir_builder builder; 131 nir_builder_init(&builder, function->impl); 132 nir_foreach_block(block, function->impl) { 133 nir_foreach_instr_safe(instr, block) { 134 if (instr->type == nir_instr_type_intrinsic) 135 progress |= lower_clip_plane_store(nir_instr_as_intrinsic(instr), 136 clip_plane_enable, 137 &builder); 138 } 139 } 140 141 nir_metadata_preserve(function->impl, nir_metadata_block_index | nir_metadata_dominance); 142 } 143 } 144 145 return progress; 146} 147