1/* 2 * Copyright 2021 Advanced Micro Devices, Inc. 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 "si_pipe.h" 25#include "nir.h" 26#include "nir_builder.h" 27#include "nir_worklist.h" 28 29 30static bool 31add_src_instr_to_worklist(nir_src *src, void *wl) 32{ 33 if (!src->is_ssa) 34 return false; 35 36 nir_instr_worklist_push_tail(wl, src->ssa->parent_instr); 37 return true; 38} 39 40static int 41get_tex_unit(nir_tex_instr *tex) 42{ 43 int tex_index = nir_tex_instr_src_index(tex, nir_tex_src_texture_deref); 44 if (tex_index >= 0) { 45 nir_deref_instr *deref = nir_src_as_deref(tex->src[tex_index].src); 46 nir_variable *var = nir_deref_instr_get_variable(deref); 47 return var ? var->data.binding : 0; 48 } 49 return -1; 50} 51 52static int 53check_instr_depends_on_tex(nir_intrinsic_instr *store) 54{ 55 int texunit = -1; 56 struct set *instrs = _mesa_set_create(NULL, _mesa_hash_pointer, 57 _mesa_key_pointer_equal); 58 nir_instr_worklist *work = nir_instr_worklist_create(); 59 60 _mesa_set_add(instrs, &store->instr); 61 add_src_instr_to_worklist(&store->src[0], work); 62 63 nir_foreach_instr_in_worklist(instr, work) { 64 /* Don't process an instruction twice */ 65 if (_mesa_set_search(instrs, instr)) 66 continue; 67 68 _mesa_set_add(instrs, instr); 69 70 if (instr->type == nir_instr_type_alu || 71 instr->type == nir_instr_type_load_const) { 72 /* TODO: ubo, etc */ 73 if (!nir_foreach_src(instr, add_src_instr_to_worklist, work)) 74 break; 75 continue; 76 } else if (instr->type == nir_instr_type_tex) { 77 if (texunit != -1) { 78 /* We can only depend on a single tex */ 79 texunit = -1; 80 break; 81 } else { 82 texunit = get_tex_unit(nir_instr_as_tex(instr)); 83 continue; 84 } 85 } else { 86 break; 87 } 88 } 89 90 nir_instr_worklist_destroy(work); 91 _mesa_set_destroy(instrs, NULL); 92 return texunit; 93} 94 95static bool 96get_output_as_const_value(nir_shader *shader, float values[4]) 97{ 98 nir_foreach_function(function, shader) { 99 nir_foreach_block_reverse(block, function->impl) { 100 nir_foreach_instr_reverse_safe(instr, block) { 101 switch (instr->type) { 102 case nir_instr_type_intrinsic: { 103 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); 104 if (intrin->intrinsic == nir_intrinsic_store_output) { 105 nir_const_value *c = nir_src_as_const_value(intrin->src[0]); 106 if (c) { 107 nir_const_value_to_array(values, c, 4, f32); 108 return true; 109 } 110 return false; 111 } 112 FALLTHROUGH; 113 } 114 default: 115 continue; 116 } 117 } 118 } 119 } 120 return false; 121} 122 123struct replace_param { 124 float value[4]; 125 int *texunit; 126}; 127 128static bool 129store_instr_depends_on_tex(nir_builder *b, nir_instr *instr, void *state) 130{ 131 if (instr->type != nir_instr_type_intrinsic) 132 return false; 133 134 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); 135 if (intrin->intrinsic != nir_intrinsic_store_output) 136 return false; 137 138 struct replace_param *p = (struct replace_param*) state; 139 *(p->texunit) = check_instr_depends_on_tex(intrin); 140 141 return *(p->texunit) != -1; 142} 143 144 145static bool 146replace_tex_by_imm(nir_builder *b, nir_instr *instr, void *state) 147{ 148 if (instr->type != nir_instr_type_tex) 149 return false; 150 151 nir_tex_instr *tex = nir_instr_as_tex(instr); 152 struct replace_param *p = (struct replace_param*) state; 153 154 if (get_tex_unit(tex) != *(p->texunit)) 155 return false; 156 157 b->cursor = nir_instr_remove(&tex->instr); 158 nir_ssa_def *imm = nir_imm_vec4(b, p->value[0], p->value[1], p->value[2], p->value[3]); 159 nir_ssa_def_rewrite_uses(&tex->dest.ssa, imm); 160 return true; 161} 162 163 164/* This function returns true if a shader' sole output becomes constant when 165 * a given texunit is replaced by a constant value. 166 * The input constant value is passed as 'in' and the determined constant 167 * value is stored in 'out'. The texunit is also remembered. 168 */ 169bool 170si_nir_is_output_const_if_tex_is_const(nir_shader *shader, float *in, float *out, int *texunit) 171{ 172 assert(shader->info.stage == MESA_SHADER_FRAGMENT); 173 174 if (BITSET_COUNT(shader->info.textures_used) == 0 || 175 util_bitcount64(shader->info.outputs_written) != 1) 176 return false; 177 178 struct replace_param p; 179 memcpy(p.value, in, 4 * sizeof(float)); 180 p.texunit = texunit; 181 182 /* Test if the single store_output only depends on constants and a single texture op */ 183 if (nir_shader_instructions_pass(shader, store_instr_depends_on_tex, nir_metadata_all, &p)) { 184 assert(*p.texunit != -1); 185 186 /* Replace nir_tex_instr using texunit by vec4(v) */ 187 nir_shader_instructions_pass(shader, replace_tex_by_imm, 188 nir_metadata_block_index | 189 nir_metadata_dominance, &p); 190 191 /* Optimize the cloned shader */ 192 bool progress; 193 do { 194 progress = false; 195 NIR_PASS(progress, shader, nir_copy_prop); 196 NIR_PASS(progress, shader, nir_opt_remove_phis); 197 NIR_PASS(progress, shader, nir_opt_dce); 198 NIR_PASS(progress, shader, nir_opt_dead_cf); 199 NIR_PASS(progress, shader, nir_opt_algebraic); 200 NIR_PASS(progress, shader, nir_opt_constant_folding); 201 } while (progress); 202 203 /* Is the output a constant value? */ 204 if (get_output_as_const_value(shader, out)) 205 return true; 206 } 207 208 return false; 209} 210