• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2021 Valve 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
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 /**
25  * Lower constant arrays to uniform arrays.
26  *
27  * Some driver backends (such as i965 and nouveau) don't handle constant arrays
28  * gracefully, instead treating them as ordinary writable temporary arrays.
29  * Since arrays can be large, this often means spilling them to scratch memory,
30  * which usually involves a large number of instructions.
31  *
32  * This must be called prior to gl_nir_set_uniform_initializers(); we need the
33  * linker to process our new uniform's constant initializer.
34  *
35  * This should be called after optimizations, since those can result in
36  * splitting and removing arrays that are indexed by constant expressions.
37  */
38 #include "nir.h"
39 #include "nir_builder.h"
40 #include "nir_deref.h"
41 
42 struct var_info {
43    nir_variable *var;
44 
45    bool is_constant;
46    bool found_read;
47 
48    /* Block that has all the variable stores.  All the blocks with reads
49     * should be dominated by this block.
50     */
51    nir_block *block;
52 };
53 
54 static void
set_const_initialiser(nir_deref_instr ** p,nir_constant * top_level_init,nir_src * const_src,unsigned writemask)55 set_const_initialiser(nir_deref_instr **p, nir_constant *top_level_init,
56                       nir_src *const_src, unsigned writemask)
57 {
58    assert(*p);
59 
60    nir_constant *ret = top_level_init;
61    for ( ; *p; p++) {
62       if ((*p)->deref_type == nir_deref_type_array) {
63          assert(nir_src_is_const((*p)->arr.index));
64 
65          uint64_t idx = nir_src_as_uint((*p)->arr.index);
66 
67          /* Just return if this is an out of bounds write */
68          if (idx >= ret->num_elements)
69             return;
70 
71          ret = ret->elements[idx];
72       } else if ((*p)->deref_type == nir_deref_type_struct) {
73          ret = ret->elements[(*p)->strct.index];
74       } else {
75          unreachable("Unsupported deref type");
76       }
77    }
78 
79    /* Now that we have selected the corrent nir_constant we copy the constant
80     * values to it.
81     */
82    assert(const_src->is_ssa);
83    nir_instr *src_instr = const_src->ssa->parent_instr;
84    assert(src_instr->type == nir_instr_type_load_const);
85    nir_load_const_instr* load = nir_instr_as_load_const(src_instr);
86 
87    for (unsigned i = 0; i < load->def.num_components; i++) {
88       if (!(writemask & (1 << i)))
89          continue;
90 
91       memcpy(ret->values + i, load->value + i, sizeof(*load->value));
92    }
93 
94    return;
95 }
96 
97 static nir_constant *
rebuild_const_array_initialiser(const struct glsl_type * type,void * mem_ctx)98 rebuild_const_array_initialiser(const struct glsl_type *type, void *mem_ctx)
99 {
100    nir_constant *ret = rzalloc(mem_ctx, nir_constant);
101 
102    if (glsl_type_is_matrix(type) && glsl_get_matrix_columns(type) > 1) {
103       ret->num_elements = glsl_get_matrix_columns(type);
104       ret->elements = ralloc_array(mem_ctx, nir_constant *, ret->num_elements);
105 
106       for (unsigned i = 0; i < ret->num_elements; i++) {
107          ret->elements[i] = rzalloc(mem_ctx, nir_constant);
108       }
109 
110       return ret;
111    }
112 
113    if (glsl_type_is_array(type) || glsl_type_is_struct(type)) {
114       ret->num_elements = glsl_get_length(type);
115       ret->elements = ralloc_array(mem_ctx, nir_constant *, ret->num_elements);
116 
117       for (unsigned i = 0; i < ret->num_elements; i++) {
118          if (glsl_type_is_array(type)) {
119             ret->elements[i] =
120                rebuild_const_array_initialiser(glsl_get_array_element(type), mem_ctx);
121          } else {
122             ret->elements[i] =
123                rebuild_const_array_initialiser(glsl_get_struct_field(type, i), mem_ctx);
124          }
125       }
126    }
127 
128    return ret;
129 }
130 
131 static bool
lower_const_array_to_uniform(nir_shader * shader,struct var_info * info,struct hash_table * const_array_vars,unsigned * free_uni_components,unsigned * const_count,bool * progress)132 lower_const_array_to_uniform(nir_shader *shader, struct var_info *info,
133                              struct hash_table *const_array_vars,
134                              unsigned *free_uni_components,
135                              unsigned *const_count, bool *progress)
136 {
137    nir_variable *var = info->var;
138 
139    if (!info->is_constant)
140       return true;
141 
142    if (!glsl_type_is_array(var->type))
143       return true;
144 
145    /* TODO: Add support for 8bit and 16bit types */
146    if (!glsl_type_is_32bit(glsl_without_array(var->type)) &&
147        !glsl_type_is_64bit(glsl_without_array(var->type)))
148       return true;
149 
150    /* How many uniform component slots are required? */
151    unsigned component_slots = glsl_get_component_slots(var->type);
152 
153    /* We would utilize more than is available, bail out. */
154    if (component_slots > *free_uni_components)
155       return false;
156 
157    *free_uni_components -= component_slots;
158 
159    /* In the very unlikely event of 4294967295 constant arrays in a single
160     * shader, don't promote this to a uniform.
161     */
162    unsigned limit = ~0;
163    if (*const_count == limit)
164       return false;
165 
166    nir_variable *uni = rzalloc(shader, nir_variable);
167 
168    /* Rebuild constant initialiser */
169    nir_constant *const_init = rebuild_const_array_initialiser(var->type, uni);
170 
171    /* Set constant initialiser */
172    nir_function_impl *impl = nir_shader_get_entrypoint(shader);
173    nir_foreach_block(block, impl) {
174       nir_foreach_instr(instr, block) {
175          if (instr->type != nir_instr_type_intrinsic)
176             continue;
177 
178          nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
179          assert(intrin->intrinsic != nir_intrinsic_copy_deref);
180          if (intrin->intrinsic != nir_intrinsic_store_deref)
181             continue;
182 
183          nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
184          nir_variable *deref_var = nir_deref_instr_get_variable(deref);
185          if (var != deref_var)
186             continue;
187 
188          nir_deref_path path;
189          nir_deref_path_init(&path, deref, NULL);
190          assert(path.path[0]->deref_type == nir_deref_type_var);
191 
192          nir_deref_instr **p = &path.path[1];
193          set_const_initialiser(p, const_init, &intrin->src[1],
194                                nir_intrinsic_write_mask(intrin));
195 
196          nir_deref_path_finish(&path);
197       }
198    }
199 
200    uni->constant_initializer = const_init;
201    uni->data.how_declared = nir_var_hidden;
202    uni->data.read_only = true;
203    uni->data.mode = nir_var_uniform;
204    uni->type = info->var->type;
205    uni->name = ralloc_asprintf(uni, "constarray_%x_%u",
206                                *const_count, shader->info.stage);
207 
208    nir_shader_add_variable(shader, uni);
209 
210    *const_count = *const_count + 1;
211 
212    _mesa_hash_table_insert(const_array_vars, info->var, uni);
213 
214    *progress = true;
215 
216    return true;
217 }
218 
219 static unsigned
count_uniforms(nir_shader * shader)220 count_uniforms(nir_shader *shader)
221 {
222    unsigned total = 0;
223 
224    nir_foreach_variable_with_modes(var, shader, nir_var_uniform) {
225       total += glsl_get_component_slots(var->type);
226    }
227 
228    return total;
229 }
230 
231 bool
nir_lower_const_arrays_to_uniforms(nir_shader * shader,unsigned max_uniform_components)232 nir_lower_const_arrays_to_uniforms(nir_shader *shader,
233                                    unsigned max_uniform_components)
234 {
235    /* This only works with a single entrypoint */
236    nir_function_impl *impl = nir_shader_get_entrypoint(shader);
237 
238    unsigned num_locals = nir_function_impl_index_vars(impl);
239    if (num_locals == 0) {
240       nir_shader_preserve_all_metadata(shader);
241       return false;
242    }
243 
244    bool progress = false;
245    unsigned uniform_components = count_uniforms(shader);
246    unsigned free_uni_components = max_uniform_components - uniform_components;
247    unsigned const_count = 0;
248 
249    struct var_info *var_infos = ralloc_array(NULL, struct var_info, num_locals);
250    nir_foreach_function_temp_variable(var, impl) {
251       var_infos[var->index] = (struct var_info) {
252          .var = var,
253          .is_constant = true,
254          .found_read = false,
255       };
256    }
257 
258    nir_metadata_require(impl, nir_metadata_dominance);
259 
260    struct hash_table *const_array_vars =
261       _mesa_hash_table_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
262 
263    /* First, walk through the shader and figure out what variables we can
264     * lower to a uniform.
265     */
266    nir_foreach_block(block, impl) {
267       nir_foreach_instr(instr, block) {
268          if (instr->type == nir_instr_type_deref) {
269             /* If we ever see a complex use of a deref_var, we have to assume
270              * that variable is non-constant because we can't guarantee we
271              * will find all of the writers of that variable.
272              */
273             nir_deref_instr *deref = nir_instr_as_deref(instr);
274             if (deref->deref_type == nir_deref_type_var &&
275                 deref->var->data.mode == nir_var_function_temp &&
276                 nir_deref_instr_has_complex_use(deref, 0))
277                var_infos[deref->var->index].is_constant = false;
278             continue;
279          }
280 
281          if (instr->type != nir_instr_type_intrinsic)
282             continue;
283 
284          nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
285 
286          bool src_is_const = false;
287          nir_deref_instr *src_deref = NULL, *dst_deref = NULL;
288          switch (intrin->intrinsic) {
289          case nir_intrinsic_store_deref:
290             dst_deref = nir_src_as_deref(intrin->src[0]);
291             src_is_const = nir_src_is_const(intrin->src[1]);
292             break;
293 
294          case nir_intrinsic_load_deref:
295             src_deref = nir_src_as_deref(intrin->src[0]);
296             break;
297 
298          case nir_intrinsic_copy_deref:
299             assert(!"Lowering of copy_deref with const arrays to uniform is prohibited");
300             break;
301 
302          default:
303             continue;
304          }
305 
306          if (dst_deref && nir_deref_mode_must_be(dst_deref, nir_var_function_temp)) {
307             nir_variable *var = nir_deref_instr_get_variable(dst_deref);
308             if (var == NULL)
309                continue;
310 
311             assert(var->data.mode == nir_var_function_temp);
312 
313             struct var_info *info = &var_infos[var->index];
314             if (!info->is_constant)
315                continue;
316 
317             if (!info->block)
318                info->block = block;
319 
320             /* We only consider variables constant if they only have constant
321              * stores, all the stores come before any reads, and all stores
322              * come from the same block.  We also can't handle indirect stores.
323              */
324             if (!src_is_const || info->found_read || block != info->block ||
325                 nir_deref_instr_has_indirect(dst_deref)) {
326                info->is_constant = false;
327             }
328          }
329 
330          if (src_deref && nir_deref_mode_must_be(src_deref, nir_var_function_temp)) {
331             nir_variable *var = nir_deref_instr_get_variable(src_deref);
332             if (var == NULL)
333                continue;
334 
335             assert(var->data.mode == nir_var_function_temp);
336 
337             /* We only consider variables constant if all the reads are
338              * dominated by the block that writes to it.
339              */
340             struct var_info *info = &var_infos[var->index];
341             if (!info->is_constant)
342                continue;
343 
344             if (!info->block || !nir_block_dominates(info->block, block))
345                info->is_constant = false;
346 
347             info->found_read = true;
348          }
349       }
350    }
351 
352    /* Now lower the constants to uniforms */
353    for (int i = 0; i < num_locals; i++) {
354       struct var_info *info = &var_infos[i];
355       if (!lower_const_array_to_uniform(shader, info, const_array_vars,
356                                         &free_uni_components, &const_count,
357                                         &progress))
358          break;
359    }
360 
361    /* Finally rewrite its uses */
362    nir_builder b;
363    nir_builder_init(&b, impl);
364    nir_foreach_block(block, impl) {
365       nir_foreach_instr_safe(instr, block) {
366 
367          if (instr->type != nir_instr_type_intrinsic)
368             continue;
369 
370          nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
371          if (intrin->intrinsic != nir_intrinsic_load_deref)
372             continue;
373 
374          nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
375          nir_variable *var = nir_deref_instr_get_variable(deref);
376 
377          struct hash_entry *entry =
378             _mesa_hash_table_search(const_array_vars, var);
379          if (!entry)
380             continue;
381 
382          b.cursor = nir_before_instr(instr);
383 
384          nir_variable *uni = (nir_variable *) entry->data;
385          nir_deref_instr *new_deref_instr = nir_build_deref_var(&b, uni);
386 
387          nir_deref_path path;
388          nir_deref_path_init(&path, deref, NULL);
389          assert(path.path[0]->deref_type == nir_deref_type_var);
390 
391          nir_deref_instr **p = &path.path[1];
392          for (; *p; p++) {
393             if ((*p)->deref_type == nir_deref_type_array) {
394                new_deref_instr = nir_build_deref_array(&b, new_deref_instr,
395                                                        (*p)->arr.index.ssa);
396             } else if ((*p)->deref_type == nir_deref_type_struct) {
397                new_deref_instr = nir_build_deref_struct(&b, new_deref_instr,
398                                                         (*p)->strct.index);
399             } else {
400                unreachable("Unsupported deref type");
401             }
402          }
403          nir_deref_path_finish(&path);
404 
405          nir_ssa_def *new_def = nir_load_deref(&b, new_deref_instr);
406 
407          nir_ssa_def_rewrite_uses(&intrin->dest.ssa, new_def);
408          nir_instr_remove(&intrin->instr);
409       }
410    }
411 
412    nir_metadata_preserve(impl, nir_metadata_block_index |
413                                nir_metadata_dominance);
414 
415    ralloc_free(var_infos);
416    _mesa_hash_table_destroy(const_array_vars, NULL);
417 
418    return progress;
419 }
420