• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013 Intel 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  * \file lower_named_interface_blocks.cpp
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 "glsl_symbol_table.h"
63 #include "ir.h"
64 #include "ir_optimization.h"
65 #include "ir_rvalue_visitor.h"
66 #include "util/hash_table.h"
67 #include "main/shader_types.h"
68 
69 static const glsl_type *
process_array_type(const glsl_type * type,unsigned idx)70 process_array_type(const glsl_type *type, unsigned idx)
71 {
72    const glsl_type *element_type = type->fields.array;
73    if (element_type->is_array()) {
74       const glsl_type *new_array_type = process_array_type(element_type, idx);
75       return glsl_type::get_array_instance(new_array_type, type->length);
76    } else {
77       return glsl_type::get_array_instance(
78          element_type->fields.structure[idx].type, type->length);
79    }
80 }
81 
82 static ir_rvalue *
process_array_ir(void * const mem_ctx,ir_dereference_array * deref_array_prev,ir_rvalue * deref_var)83 process_array_ir(void * const mem_ctx,
84                  ir_dereference_array *deref_array_prev,
85                  ir_rvalue *deref_var)
86 {
87    ir_dereference_array *deref_array =
88       deref_array_prev->array->as_dereference_array();
89 
90    if (deref_array == NULL) {
91       return new(mem_ctx) ir_dereference_array(deref_var,
92                                                deref_array_prev->array_index);
93    } else {
94       deref_array = (ir_dereference_array *) process_array_ir(mem_ctx,
95                                                               deref_array,
96                                                               deref_var);
97       return new(mem_ctx) ir_dereference_array(deref_array,
98                                                deref_array_prev->array_index);
99    }
100 }
101 
102 namespace {
103 
104 class flatten_named_interface_blocks_declarations : public ir_rvalue_visitor
105 {
106 public:
107    void * const mem_ctx;
108    hash_table *interface_namespace;
109 
flatten_named_interface_blocks_declarations(void * mem_ctx)110    flatten_named_interface_blocks_declarations(void *mem_ctx)
111       : mem_ctx(mem_ctx),
112         interface_namespace(NULL)
113    {
114    }
115 
116    void run(exec_list *instructions);
117 
118    virtual ir_visitor_status visit_leave(ir_assignment *);
119    virtual ir_visitor_status visit_leave(ir_expression *);
120    virtual void handle_rvalue(ir_rvalue **rvalue);
121 };
122 
123 } /* anonymous namespace */
124 
125 void
run(exec_list * instructions)126 flatten_named_interface_blocks_declarations::run(exec_list *instructions)
127 {
128    interface_namespace = _mesa_hash_table_create(NULL, _mesa_hash_string,
129                                                  _mesa_key_string_equal);
130 
131    /* First pass: adjust instance block variables with an instance name
132     * to not have an instance name.
133     *
134     * The interface block variables are stored in the interface_namespace
135     * hash table so they can be used in the second pass.
136     */
137    foreach_in_list_safe(ir_instruction, node, instructions) {
138       ir_variable *var = node->as_variable();
139       if (!var || !var->is_interface_instance())
140          continue;
141 
142       /* It should be possible to handle uniforms during this pass,
143        * but, this will require changes to the other uniform block
144        * support code.
145        */
146       if (var->data.mode == ir_var_uniform ||
147           var->data.mode == ir_var_shader_storage)
148          continue;
149 
150       const glsl_type * iface_t = var->type->without_array();
151       exec_node *insert_pos = var;
152 
153       assert (iface_t->is_interface());
154 
155       for (unsigned i = 0; i < iface_t->length; i++) {
156          const char * field_name = iface_t->fields.structure[i].name;
157          char *iface_field_name =
158             ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
159                             var->data.mode == ir_var_shader_in ? "in" : "out",
160                             iface_t->name, var->name, field_name);
161 
162          hash_entry *entry = _mesa_hash_table_search(interface_namespace,
163                                                      iface_field_name);
164          ir_variable *found_var = entry ? (ir_variable *) entry->data : NULL;
165          if (!found_var) {
166             ir_variable *new_var;
167             char *var_name =
168                ralloc_strdup(mem_ctx, iface_t->fields.structure[i].name);
169             if (!var->type->is_array()) {
170                new_var =
171                   new(mem_ctx) ir_variable(iface_t->fields.structure[i].type,
172                                            var_name,
173                                            (ir_variable_mode) var->data.mode);
174             } else {
175                const glsl_type *new_array_type =
176                   process_array_type(var->type, i);
177                new_var =
178                   new(mem_ctx) ir_variable(new_array_type,
179                                            var_name,
180                                            (ir_variable_mode) var->data.mode);
181             }
182             new_var->data.location = iface_t->fields.structure[i].location;
183             new_var->data.location_frac =
184                iface_t->fields.structure[i].component >= 0 ?
185                   iface_t->fields.structure[i].component : 0;
186             new_var->data.explicit_location = (new_var->data.location >= 0);
187             new_var->data.explicit_component =
188                (iface_t->fields.structure[i].component >= 0);
189             new_var->data.offset = iface_t->fields.structure[i].offset;
190             new_var->data.explicit_xfb_offset =
191                (iface_t->fields.structure[i].offset >= 0);
192             new_var->data.xfb_buffer =
193                iface_t->fields.structure[i].xfb_buffer;
194             new_var->data.explicit_xfb_buffer =
195                iface_t->fields.structure[i].explicit_xfb_buffer;
196             new_var->data.interpolation =
197                iface_t->fields.structure[i].interpolation;
198             new_var->data.centroid = iface_t->fields.structure[i].centroid;
199             new_var->data.sample = iface_t->fields.structure[i].sample;
200             new_var->data.patch = iface_t->fields.structure[i].patch;
201             new_var->data.stream = var->data.stream;
202             new_var->data.how_declared = var->data.how_declared;
203             new_var->data.from_named_ifc_block = 1;
204 
205             new_var->init_interface_type(var->type);
206             _mesa_hash_table_insert(interface_namespace, iface_field_name,
207                                     new_var);
208             insert_pos->insert_after(new_var);
209             insert_pos = new_var;
210          }
211       }
212       var->remove();
213    }
214 
215    /* Second pass: visit all ir_dereference_record instances, and if they
216     * reference an interface block, then flatten the refererence out.
217     */
218    visit_list_elements(this, instructions);
219    _mesa_hash_table_destroy(interface_namespace, NULL);
220    interface_namespace = NULL;
221 }
222 
223 ir_visitor_status
visit_leave(ir_assignment * ir)224 flatten_named_interface_blocks_declarations::visit_leave(ir_assignment *ir)
225 {
226    ir_dereference_record *lhs_rec = ir->lhs->as_dereference_record();
227 
228    ir_variable *lhs_var =  ir->lhs->variable_referenced();
229    if (lhs_var && lhs_var->get_interface_type()) {
230       lhs_var->data.assigned = 1;
231    }
232 
233    if (lhs_rec) {
234       ir_rvalue *lhs_rec_tmp = lhs_rec;
235       handle_rvalue(&lhs_rec_tmp);
236       if (lhs_rec_tmp != lhs_rec) {
237          ir->set_lhs(lhs_rec_tmp);
238       }
239 
240       ir_variable *lhs_var =  lhs_rec_tmp->variable_referenced();
241       if (lhs_var) {
242          lhs_var->data.assigned = 1;
243       }
244    }
245    return rvalue_visit(ir);
246 }
247 
248 ir_visitor_status
visit_leave(ir_expression * ir)249 flatten_named_interface_blocks_declarations::visit_leave(ir_expression *ir)
250 {
251    ir_visitor_status status = rvalue_visit(ir);
252 
253    if (ir->operation == ir_unop_interpolate_at_centroid ||
254        ir->operation == ir_binop_interpolate_at_offset ||
255        ir->operation == ir_binop_interpolate_at_sample) {
256       const ir_rvalue *val = ir->operands[0];
257 
258       /* This disables varying packing for this input. */
259       val->variable_referenced()->data.must_be_shader_input = 1;
260    }
261 
262    return status;
263 }
264 
265 void
handle_rvalue(ir_rvalue ** rvalue)266 flatten_named_interface_blocks_declarations::handle_rvalue(ir_rvalue **rvalue)
267 {
268    if (*rvalue == NULL)
269       return;
270 
271    ir_dereference_record *ir = (*rvalue)->as_dereference_record();
272    if (ir == NULL)
273       return;
274 
275    ir_variable *var = ir->variable_referenced();
276    if (var == NULL)
277       return;
278 
279    if (!var->is_interface_instance())
280       return;
281 
282    /* It should be possible to handle uniforms during this pass,
283     * but, this will require changes to the other uniform block
284     * support code.
285     */
286    if (var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage)
287       return;
288 
289    if (var->get_interface_type() != NULL) {
290       char *iface_field_name =
291          ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
292                          var->data.mode == ir_var_shader_in ? "in" : "out",
293                          var->get_interface_type()->name,
294                          var->name,
295                          ir->record->type->fields.structure[ir->field_idx].name);
296 
297       /* Find the variable in the set of flattened interface blocks */
298       hash_entry *entry = _mesa_hash_table_search(interface_namespace,
299                                                   iface_field_name);
300       assert(entry);
301       ir_variable *found_var = (ir_variable *) entry->data;
302 
303       ir_dereference_variable *deref_var =
304          new(mem_ctx) ir_dereference_variable(found_var);
305 
306       ir_dereference_array *deref_array =
307          ir->record->as_dereference_array();
308       if (deref_array != NULL) {
309          *rvalue = process_array_ir(mem_ctx, deref_array,
310                                     (ir_rvalue *)deref_var);
311       } else {
312          *rvalue = deref_var;
313       }
314    }
315 }
316 
317 void
lower_named_interface_blocks(void * mem_ctx,gl_linked_shader * shader)318 lower_named_interface_blocks(void *mem_ctx, gl_linked_shader *shader)
319 {
320    flatten_named_interface_blocks_declarations v_decl(mem_ctx);
321    v_decl.run(shader->ir);
322 }
323 
324