xref: /aosp_15_r20/external/mesa3d/src/compiler/glsl/gl_nir_lower_named_interface_blocks.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2013 Intel Corporation
3  * Copyright © 2023 Valve Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 /**
26  *
27  * This lowering pass converts all interface blocks with instance names
28  * into interface blocks without an instance name.
29  *
30  * For example, the following shader:
31  *
32  *   out block {
33  *     float block_var;
34  *   } inst_name;
35  *
36  *   main()
37  *   {
38  *     inst_name.block_var = 0.0;
39  *   }
40  *
41  * Is rewritten to:
42  *
43  *   out block {
44  *     float block_var;
45  *   };
46  *
47  *   main()
48  *   {
49  *     block_var = 0.0;
50  *   }
51  *
52  * This takes place after the shader code has already been verified with
53  * the interface name in place.
54  *
55  * The linking phase will use the interface block name rather than the
56  * interface's instance name when linking interfaces.
57  *
58  * This modification to the ir allows our currently existing dead code
59  * elimination to work with interface blocks without changes.
60  */
61 
62 #include "nir.h"
63 #include "nir_builder.h"
64 #include "nir_deref.h"
65 #include "gl_nir.h"
66 #include "glsl_types.h"
67 
68 #include "main/shader_types.h"
69 
70 struct lower_named_block_state {
71    void *mem_ctx;
72    struct hash_table *interface_namespace;
73 };
74 
75 static const struct glsl_type *
process_array_type(const struct glsl_type * type,unsigned idx)76 process_array_type(const struct glsl_type *type, unsigned idx)
77 {
78    const struct glsl_type *element_type = glsl_get_array_element(type);
79    unsigned length = glsl_get_length(type);
80    if (glsl_type_is_array(element_type)) {
81       const struct glsl_type *new_array_type =
82          process_array_type(element_type, idx);
83       return  glsl_array_type(new_array_type, length, 0);
84    } else {
85       return glsl_array_type(glsl_get_struct_field(element_type, idx),
86                              length, 0);
87    }
88 }
89 
90 static nir_deref_instr *
process_derefs(nir_builder * b,nir_deref_instr ** p,nir_deref_instr * parent)91 process_derefs(nir_builder *b, nir_deref_instr **p, nir_deref_instr *parent)
92 {
93    bool found_ifc = false;
94    for (; *p; p++) {
95       if ((*p)->deref_type == nir_deref_type_array) {
96          parent = nir_build_deref_array(b, parent, (*p)->arr.index.ssa);
97       } else if ((*p)->deref_type == nir_deref_type_struct) {
98          if (!found_ifc) {
99             /* We found the interface block so just skip it */
100             found_ifc = true;
101             continue;
102          } else {
103             parent = nir_build_deref_struct(b, parent, (*p)->strct.index);
104          }
105       } else if ((*p)->deref_type == nir_deref_type_array_wildcard) {
106          parent = nir_build_deref_array_wildcard(b, parent);
107       }
108    }
109 
110    return parent;
111 }
112 
113 /* Disable packing on varyings used by interpolate functions. This must be
114  * called after lowering blocks to avoid disabling packing on the entire
115  * block.
116  */
117 static void
disable_varying_packing_when_used_by_interpolate_functions(nir_intrinsic_instr * intr,nir_variable * var)118 disable_varying_packing_when_used_by_interpolate_functions(nir_intrinsic_instr *intr,
119                                                            nir_variable *var)
120 {
121    if (intr->intrinsic == nir_intrinsic_interp_deref_at_centroid ||
122        intr->intrinsic == nir_intrinsic_interp_deref_at_sample ||
123        intr->intrinsic == nir_intrinsic_interp_deref_at_offset) {
124 
125       /* This disables varying packing for this input. */
126       var->data.must_be_shader_input = 1;
127    }
128 }
129 
130 static char *
create_ifc_field_name_str(void * mem_ctx,nir_variable * var,const struct glsl_type * iface_t,unsigned f_idx)131 create_ifc_field_name_str(void *mem_ctx, nir_variable *var,
132                           const struct glsl_type *iface_t, unsigned f_idx)
133 {
134    char *iface_field_name =
135       ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
136                       var->data.mode == nir_var_shader_in ? "in" : "out",
137                       glsl_get_type_name(iface_t), var->name,
138                       glsl_get_struct_elem_name(iface_t, f_idx));
139 
140    return iface_field_name;
141 }
142 
143 static bool
flatten_named_interface_deref(void * mem_ctx,nir_builder * b,nir_deref_instr * deref,nir_intrinsic_instr * intr,struct hash_table * interface_namespace,bool is_src0)144 flatten_named_interface_deref(void *mem_ctx, nir_builder *b,
145                               nir_deref_instr *deref,
146                               nir_intrinsic_instr *intr,
147                               struct hash_table *interface_namespace,
148                               bool is_src0)
149 {
150    nir_variable_mode mask = nir_var_shader_in | nir_var_shader_out;
151 
152    if (!nir_deref_mode_is_one_of(deref, mask))
153       return false;
154 
155    nir_variable *var = nir_deref_instr_get_variable(deref);
156 
157    const struct glsl_type * iface_t = glsl_without_array(var->type);
158    if (iface_t != var->interface_type)
159       return false;
160 
161    nir_deref_path path;
162    nir_deref_path_init(&path, deref, NULL);
163 
164    assert(path.path[0]->deref_type == nir_deref_type_var);
165    nir_deref_instr **p = &path.path[1];
166 
167    char *iface_field_name = NULL;
168    for (; *p; p++) {
169       if ((*p)->deref_type == nir_deref_type_struct) {
170          iface_field_name =
171             create_ifc_field_name_str(mem_ctx, var, iface_t,
172                                       (*p)->strct.index);
173          break;
174       }
175    }
176    assert(iface_field_name);
177 
178    /* Find the variable in the set of flattened interface blocks */
179    struct hash_entry *entry =
180       _mesa_hash_table_search(interface_namespace, iface_field_name);
181    assert(entry);
182 
183    nir_variable *found_var = (nir_variable *) entry->data;
184 
185    if (intr->intrinsic == nir_intrinsic_store_deref ||
186        (intr->intrinsic == nir_intrinsic_copy_deref && is_src0))
187       found_var->data.assigned = 1;
188 
189    b->cursor = nir_before_instr(&intr->instr);
190    nir_deref_instr *deref_var = nir_build_deref_var(b, found_var);
191    if (glsl_type_is_array(found_var->type) ||
192        glsl_type_is_struct(found_var->type) ||
193        glsl_type_is_matrix(found_var->type)) {
194       p = &path.path[1];
195       deref_var = process_derefs(b, p, deref_var);
196    }
197 
198    disable_varying_packing_when_used_by_interpolate_functions(intr,
199                                                               found_var);
200 
201    nir_deref_path_finish(&path);
202 
203    nir_def_rewrite_uses(&deref->def, &deref_var->def);
204 
205    return true;
206 }
207 
208 static bool
flatten_named_interface_derefs(nir_builder * b,nir_intrinsic_instr * intr,void * cb_data)209 flatten_named_interface_derefs(nir_builder *b, nir_intrinsic_instr *intr,
210                                void *cb_data)
211 {
212    struct lower_named_block_state *state =
213       (struct lower_named_block_state *) cb_data;
214 
215    if (intr->intrinsic != nir_intrinsic_copy_deref &&
216        intr->intrinsic != nir_intrinsic_load_deref &&
217        intr->intrinsic != nir_intrinsic_store_deref &&
218        intr->intrinsic != nir_intrinsic_interp_deref_at_centroid &&
219        intr->intrinsic != nir_intrinsic_interp_deref_at_sample &&
220        intr->intrinsic != nir_intrinsic_interp_deref_at_offset &&
221        intr->intrinsic != nir_intrinsic_interp_deref_at_vertex)
222       return false;
223 
224    nir_deref_instr *deref = nir_src_as_deref(intr->src[0]);
225    bool progress =
226       flatten_named_interface_deref(state->mem_ctx, b, deref, intr,
227                                     state->interface_namespace, true);
228 
229    if (intr->intrinsic == nir_intrinsic_copy_deref) {
230       deref = nir_src_as_deref(intr->src[1]);
231       progress |=
232          flatten_named_interface_deref(state->mem_ctx, b, deref, intr,
233                                        state->interface_namespace, false);
234    }
235 
236    return progress;
237 }
238 
239 static void
lower_named_interface_blocks(struct gl_linked_shader * sh)240 lower_named_interface_blocks(struct gl_linked_shader *sh)
241 {
242    void *mem_ctx = ralloc_context(NULL);
243 
244    struct hash_table *interface_namespace =
245       _mesa_hash_table_create(mem_ctx, _mesa_hash_string,
246                               _mesa_key_string_equal);
247 
248    /* First pass: adjust instance block variables with an instance name
249     * to not have an instance name.
250     *
251     * The interface block variables are stored in the interface_namespace
252     * hash table so they can be used in the second pass.
253     */
254    nir_foreach_variable_with_modes_safe(var, sh->Program->nir,
255                                         nir_var_shader_in | nir_var_shader_out) {
256       const struct glsl_type * iface_t = glsl_without_array(var->type);
257       if (iface_t != var->interface_type)
258          continue;
259 
260       for (unsigned i = 0; i < iface_t->length; i++) {
261          const char *field_name = glsl_get_struct_elem_name(iface_t, i);
262          char *iface_field_name =
263             create_ifc_field_name_str(mem_ctx, var, iface_t, i);
264 
265          struct hash_entry *entry = _mesa_hash_table_search(interface_namespace,
266                                                             iface_field_name);
267          nir_variable *found_var = entry ? (nir_variable *) entry->data : NULL;
268          if (!found_var) {
269             const struct glsl_struct_field *field_data =
270                glsl_get_struct_field_data(iface_t, i);
271 
272             nir_variable *new_var = rzalloc(sh->Program->nir, nir_variable);
273             new_var->name = ralloc_strdup(new_var, field_name);
274             if (!glsl_type_is_array(var->type)) {
275                new_var->type =  glsl_get_struct_field(iface_t, i);
276             } else {
277                new_var->type = process_array_type(var->type, i);
278             }
279             new_var->data.mode = var->data.mode;
280             new_var->data.location = field_data->location;
281             new_var->data.location_frac = field_data->component >= 0 ?
282                field_data->component : 0;
283             new_var->data.explicit_location = (new_var->data.location >= 0);
284             new_var->data.offset = field_data->offset;
285             new_var->data.explicit_offset = (field_data->offset >= 0);
286             new_var->data.xfb.buffer = field_data->xfb_buffer;
287             new_var->data.explicit_xfb_buffer = field_data->explicit_xfb_buffer;
288             new_var->data.interpolation = field_data->interpolation;
289             new_var->data.centroid = field_data->centroid;
290             new_var->data.sample = field_data->sample;
291             new_var->data.patch = field_data->patch;
292             new_var->data.stream = var->data.stream;
293             new_var->data.how_declared = var->data.how_declared;
294             new_var->data.from_named_ifc_block = 1;
295 
296             new_var->interface_type = var->type;
297             _mesa_hash_table_insert(interface_namespace, iface_field_name,
298                                     new_var);
299 
300             nir_shader_add_variable(sh->Program->nir, new_var);
301          }
302       }
303    }
304 
305    /* Second pass: redirect dereferences to the new vars. */
306    struct lower_named_block_state state;
307    state.mem_ctx = mem_ctx;
308    state.interface_namespace = interface_namespace;
309    nir_shader_intrinsics_pass(sh->Program->nir, flatten_named_interface_derefs,
310                               nir_metadata_control_flow, &state);
311 
312    /* Third pass: Mark now lowered blks as ordinary globals to be dead code
313     * eliminated. Also use this oppotunity to set the compact flag where
314     * needed now that the default interface block has been lowered away.
315     */
316    nir_foreach_variable_with_modes(var, sh->Program->nir,
317                                    nir_var_shader_in | nir_var_shader_out) {
318 
319       if (var->data.mode == nir_var_shader_in) {
320          if (sh->Program->nir->info.stage == MESA_SHADER_TESS_EVAL &&
321              (var->data.location == VARYING_SLOT_TESS_LEVEL_INNER ||
322               var->data.location == VARYING_SLOT_TESS_LEVEL_OUTER)) {
323             var->data.compact =
324                glsl_type_is_scalar(glsl_without_array(var->type));
325          }
326 
327          if (sh->Program->nir->info.stage > MESA_SHADER_VERTEX &&
328              var->data.location >= VARYING_SLOT_CLIP_DIST0 &&
329              var->data.location <= VARYING_SLOT_CULL_DIST1) {
330             var->data.compact =
331                glsl_type_is_scalar(glsl_without_array(var->type));
332          }
333       } else {
334          assert(var->data.mode == nir_var_shader_out);
335 
336          if (sh->Program->nir->info.stage == MESA_SHADER_TESS_CTRL &&
337              (var->data.location == VARYING_SLOT_TESS_LEVEL_INNER ||
338               var->data.location == VARYING_SLOT_TESS_LEVEL_OUTER)) {
339             var->data.compact =
340                glsl_type_is_scalar(glsl_without_array(var->type));
341          }
342 
343          if (sh->Program->nir->info.stage <= MESA_SHADER_GEOMETRY &&
344              var->data.location >= VARYING_SLOT_CLIP_DIST0 &&
345              var->data.location <= VARYING_SLOT_CULL_DIST1) {
346             var->data.compact =
347                glsl_type_is_scalar(glsl_without_array(var->type));
348          }
349       }
350 
351       const struct glsl_type * iface_t = glsl_without_array(var->type);
352       if (!(iface_t == var->interface_type))
353          continue;
354 
355       var->data.mode = nir_var_shader_temp;
356    }
357    nir_fixup_deref_modes(sh->Program->nir);
358 
359    ralloc_free(mem_ctx);
360 }
361 
362 void
gl_nir_lower_named_interface_blocks(struct gl_shader_program * prog)363 gl_nir_lower_named_interface_blocks(struct gl_shader_program *prog)
364 {
365    for (unsigned int i = 0; i < MESA_SHADER_STAGES; i++) {
366       if (prog->_LinkedShaders[i] != NULL) {
367          NIR_PASS(_, prog->_LinkedShaders[i]->Program->nir, nir_split_var_copies);
368          lower_named_interface_blocks(prog->_LinkedShaders[i]);
369       }
370    }
371 }
372 
373