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
30 static bool
add_src_instr_to_worklist(nir_src * src,void * wl)31 add_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
40 static int
get_tex_unit(nir_tex_instr * tex)41 get_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
52 static int
check_instr_depends_on_tex(nir_intrinsic_instr * store)53 check_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
95 static bool
get_output_as_const_value(nir_shader * shader,float values[4])96 get_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
123 struct replace_param {
124 float value[4];
125 int *texunit;
126 };
127
128 static bool
store_instr_depends_on_tex(nir_builder * b,nir_instr * instr,void * state)129 store_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
145 static bool
replace_tex_by_imm(nir_builder * b,nir_instr * instr,void * state)146 replace_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 */
169 bool
si_nir_is_output_const_if_tex_is_const(nir_shader * shader,float * in,float * out,int * texunit)170 si_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