1 /* 2 * Copyright 2015 Intel Corporation 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 24 #include "nir.h" 25 #include "nir_builder.h" 26 27 /** 28 * @file 29 * 30 * This pass combines separate clip and cull distance arrays into a 31 * single array that contains both. Clip distances come first, then 32 * cull distances. It also populates nir_shader_info with the size 33 * of the original arrays so the driver knows which are which. 34 */ 35 36 /** 37 * Get the length of the clip/cull distance array, looking past 38 * any interface block arrays. 39 */ 40 static unsigned 41 get_unwrapped_array_length(nir_shader *nir, nir_variable *var) 42 { 43 if (!var) 44 return 0; 45 46 /* Unwrap GS input and TCS input/output interfaces. We want the 47 * underlying clip/cull distance array length, not the per-vertex 48 * array length. 49 */ 50 const struct glsl_type *type = var->type; 51 if (nir_is_per_vertex_io(var, nir->stage)) 52 type = glsl_get_array_element(type); 53 54 assert(glsl_type_is_array(type)); 55 56 return glsl_get_length(type); 57 } 58 59 /** 60 * Update the type of the combined array (including interface block nesting). 61 */ 62 static void 63 update_type(nir_variable *var, gl_shader_stage stage, unsigned length) 64 { 65 const struct glsl_type *type = glsl_array_type(glsl_float_type(), length); 66 67 if (nir_is_per_vertex_io(var, stage)) 68 type = glsl_array_type(type, glsl_get_length(var->type)); 69 70 var->type = type; 71 } 72 73 /** 74 * Rewrite any clip/cull distances to refer to the new combined array. 75 */ 76 static void 77 rewrite_references(nir_instr *instr, 78 nir_variable *combined, 79 unsigned cull_offset) 80 { 81 if (instr->type != nir_instr_type_intrinsic) 82 return; 83 84 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); 85 86 /* copy_var needs to be lowered to load/store before calling this pass */ 87 assert(intrin->intrinsic != nir_intrinsic_copy_var); 88 89 if (intrin->intrinsic != nir_intrinsic_load_var && 90 intrin->intrinsic != nir_intrinsic_store_var) 91 return; 92 93 nir_deref_var *var_ref = intrin->variables[0]; 94 if (var_ref->var->data.mode != combined->data.mode) 95 return; 96 97 if (var_ref->var->data.location != VARYING_SLOT_CLIP_DIST0 && 98 var_ref->var->data.location != VARYING_SLOT_CULL_DIST0) 99 return; 100 101 /* Update types along the deref chain */ 102 const struct glsl_type *type = combined->type; 103 nir_deref *deref = &var_ref->deref; 104 while (deref) { 105 deref->type = type; 106 deref = deref->child; 107 type = glsl_get_array_element(type); 108 } 109 110 /* For cull distances, add an offset to the array index */ 111 if (var_ref->var->data.location == VARYING_SLOT_CULL_DIST0) { 112 nir_deref *tail = nir_deref_tail(&intrin->variables[0]->deref); 113 nir_deref_array *array_ref = nir_deref_as_array(tail); 114 115 array_ref->base_offset += cull_offset; 116 } 117 118 /* Point the deref at the combined array */ 119 var_ref->var = combined; 120 121 /* There's no need to update writemasks; it's a scalar array. */ 122 } 123 124 static void 125 combine_clip_cull(nir_shader *nir, 126 struct exec_list *vars, 127 bool store_info) 128 { 129 nir_variable *cull = NULL; 130 nir_variable *clip = NULL; 131 132 nir_foreach_variable(var, vars) { 133 if (var->data.location == VARYING_SLOT_CLIP_DIST0) 134 clip = var; 135 136 if (var->data.location == VARYING_SLOT_CULL_DIST0) 137 cull = var; 138 } 139 140 const unsigned clip_array_size = get_unwrapped_array_length(nir, clip); 141 const unsigned cull_array_size = get_unwrapped_array_length(nir, cull); 142 143 if (store_info) { 144 nir->info->clip_distance_array_size = clip_array_size; 145 nir->info->cull_distance_array_size = cull_array_size; 146 } 147 148 if (clip) 149 clip->data.compact = true; 150 151 if (cull) 152 cull->data.compact = true; 153 154 if (cull_array_size > 0) { 155 if (clip_array_size == 0) { 156 /* No clip distances, just change the cull distance location */ 157 cull->data.location = VARYING_SLOT_CLIP_DIST0; 158 } else { 159 /* Turn the ClipDistance array into a combined one */ 160 update_type(clip, nir->stage, clip_array_size + cull_array_size); 161 162 /* Rewrite CullDistance to reference the combined array */ 163 nir_foreach_function(function, nir) { 164 if (function->impl) { 165 nir_foreach_block(block, function->impl) { 166 nir_foreach_instr(instr, block) { 167 rewrite_references(instr, clip, clip_array_size); 168 } 169 } 170 } 171 } 172 173 /* Delete the old CullDistance variable */ 174 exec_node_remove(&cull->node); 175 ralloc_free(cull); 176 } 177 } 178 } 179 180 void 181 nir_lower_clip_cull_distance_arrays(nir_shader *nir) 182 { 183 if (nir->stage <= MESA_SHADER_GEOMETRY) 184 combine_clip_cull(nir, &nir->outputs, true); 185 186 if (nir->stage > MESA_SHADER_VERTEX) 187 combine_clip_cull(nir, &nir->inputs, false); 188 } 189