• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2019 Collabora Ltd.
3  * Copyright © 2022 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 #include "nir.h"
26 #include "nir_builder.h"
27 #include "gl_nir_linker.h"
28 #include "main/shader_types.h"
29 #include "util/strndup.h"
30 
31 static char*
get_field_name(const char * name)32 get_field_name(const char *name)
33 {
34    const char *first_dot = strchr(name, '.');
35    const char *first_square_bracket = strchr(name, '[');
36    int name_size = 0;
37 
38    if (!first_square_bracket && !first_dot)
39       name_size = strlen(name);
40    else if ((!first_square_bracket ||
41             (first_dot && first_dot < first_square_bracket)))
42       name_size = first_dot - name;
43    else
44       name_size = first_square_bracket - name;
45 
46    return strndup(name, name_size);
47 }
48 
49 /* Generate a new name given the old xfb declaration string by replacing dots
50  * with '_', brackets with '@' and appending "-xfb" */
51 static char *
generate_new_name(void * mem_ctx,const char * name)52 generate_new_name(void *mem_ctx, const char *name)
53 {
54    char *new_name;
55    unsigned i = 0;
56 
57    new_name = ralloc_strdup(mem_ctx, name);
58    while (new_name[i]) {
59       if (new_name[i] == '.') {
60          new_name[i] = '_';
61       } else if (new_name[i] == '[' || new_name[i] == ']') {
62          new_name[i] = '@';
63       }
64       i++;
65    }
66 
67    if (!ralloc_strcat(&new_name, "-xfb")) {
68       ralloc_free(new_name);
69       return NULL;
70    }
71 
72    return new_name;
73 }
74 
75 /* Get the dereference for the given variable name. The method is called
76  * recursively to parse array indices and struct members. */
77 static bool
get_deref(nir_builder * b,const char * name,nir_variable * toplevel_var,nir_deref_instr ** deref,const struct glsl_type ** type)78 get_deref(nir_builder *b, const char *name, nir_variable *toplevel_var,
79           nir_deref_instr **deref, const struct glsl_type **type)
80 {
81    if (name[0] == '\0') {
82       /* End */
83       return (*deref != NULL);
84    } else if (name[0] == '[') {
85       /* Array index */
86       char *endptr = NULL;
87       unsigned index = strtol(name + 1, &endptr, 10);
88       assert(*type != NULL && glsl_type_is_array(*type) && endptr[0] == ']');
89 
90       nir_load_const_instr *c = nir_load_const_instr_create(b->shader, 1, 32);
91       c->value[0].u32 = index;
92       nir_builder_instr_insert(b, &c->instr);
93 
94       *deref = nir_build_deref_array(b, *deref, &c->def);
95       *type = glsl_without_array(*type);
96       return get_deref(b, endptr + 1, NULL, deref, type);
97    } else if (name[0] == '.') {
98       /* Struct member */
99       char *field = get_field_name(name + 1);
100 
101       assert(*type != NULL && glsl_type_is_struct(*type) && field != NULL);
102 
103       int idx = glsl_get_field_index(*type, field);
104       *deref = nir_build_deref_struct(b, *deref, idx);
105       *type = glsl_get_struct_field(*type, idx);
106       name += 1 + strlen(field);
107       free(field);
108       return get_deref(b, name, NULL, deref, type);
109    } else {
110       /* Top level variable */
111       char *field = get_field_name(name);
112       name += strlen(field);
113       free(field);
114       if (toplevel_var == NULL) {
115          return false;
116       }
117 
118       *deref = nir_build_deref_var(b, toplevel_var);
119       *type = toplevel_var->type;
120       return get_deref(b, name, NULL, deref, type);
121    }
122 }
123 
124 static void
copy_to_new_var(nir_builder * b,nir_deref_instr * deref,nir_deref_instr * new_var_deref,const struct glsl_type * type)125 copy_to_new_var(nir_builder *b, nir_deref_instr *deref,
126                 nir_deref_instr *new_var_deref, const struct glsl_type *type)
127 {
128    bool is_matrix = glsl_type_is_matrix(type);
129    unsigned components = glsl_get_vector_elements(type);
130    unsigned writemask = (1 << components) - 1;
131 
132    if (is_matrix) {
133       unsigned array_size = glsl_get_length(type);
134       for (unsigned i = 0; i < array_size; i++) {
135          nir_load_const_instr *c = nir_load_const_instr_create(b->shader, 1, 32);
136          c->value[0].u32 = i;
137          nir_builder_instr_insert(b, &c->instr);
138 
139          nir_deref_instr *m_deref = nir_build_deref_array(b, deref, &c->def);
140          nir_deref_instr *new_var_m_deref =
141             nir_build_deref_array(b, new_var_deref, &c->def);
142 
143          nir_ssa_def *value = nir_load_deref(b, m_deref);
144          nir_store_deref(b, new_var_m_deref, value, writemask);
145       }
146    } else {
147       nir_ssa_def *value = nir_load_deref(b, deref);
148       nir_store_deref(b, new_var_deref, value, writemask);
149    }
150 }
151 
152 nir_variable *
gl_nir_lower_xfb_varying(nir_shader * shader,const char * old_var_name,nir_variable * toplevel_var)153 gl_nir_lower_xfb_varying(nir_shader *shader, const char *old_var_name,
154                          nir_variable *toplevel_var)
155 {
156    nir_function_impl *impl = nir_shader_get_entrypoint(shader);
157 
158    nir_builder b;
159    nir_builder_init(&b, impl);
160    b.cursor = nir_before_block(nir_start_block(impl));
161 
162    nir_deref_instr *deref = NULL;
163    const struct glsl_type *type = NULL;
164    if (!get_deref(&b, old_var_name, toplevel_var, &deref, &type))
165       return NULL;
166 
167    nir_variable *new_variable = rzalloc(shader, nir_variable);
168    new_variable->name = generate_new_name(new_variable, old_var_name);
169    new_variable->type = type;
170    new_variable->data.mode = nir_var_shader_out;
171    new_variable->data.location = -1;
172    new_variable->data.xfb.buffer = -1;
173    new_variable->data.xfb.stride = -1;
174    new_variable->data.assigned = true;
175    nir_shader_add_variable(shader, new_variable);
176    nir_deref_instr *new_var_deref = nir_build_deref_var(&b, new_variable);
177 
178    nir_foreach_block(block, impl) {
179       if (shader->info.stage != MESA_SHADER_GEOMETRY) {
180          /* For shaders other than geometry, outputs need to be lowered before
181           * each return statement and at the end of main()
182           */
183          if (nir_block_ends_in_return_or_halt(block)) {
184             b.cursor = nir_before_instr(nir_block_last_instr(block));
185             copy_to_new_var(&b, deref, new_var_deref, type);
186          } else if (block == nir_impl_last_block(impl)) {
187             b.cursor = nir_after_instr(nir_block_last_instr(block));
188             copy_to_new_var(&b, deref, new_var_deref, type);
189          }
190       } else {
191         /* For geometry shaders, outputs need to be lowered before each call
192          * to EmitVertex()
193          */
194          nir_foreach_instr_safe(instr, block) {
195             if (instr->type != nir_instr_type_intrinsic)
196                continue;
197 
198             nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
199             if (intrin->intrinsic != nir_intrinsic_emit_vertex)
200                continue;
201 
202             b.cursor = nir_before_instr(instr);
203             copy_to_new_var(&b, deref, new_var_deref, type);
204          }
205       }
206    }
207 
208    return new_variable;
209 }
210