diff options
Diffstat (limited to 'src/compiler/glsl/lower_named_interface_blocks.cpp')
-rw-r--r-- | src/compiler/glsl/lower_named_interface_blocks.cpp | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/src/compiler/glsl/lower_named_interface_blocks.cpp b/src/compiler/glsl/lower_named_interface_blocks.cpp new file mode 100644 index 00000000000..f29eba4f75f --- /dev/null +++ b/src/compiler/glsl/lower_named_interface_blocks.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file lower_named_interface_blocks.cpp + * + * This lowering pass converts all interface blocks with instance names + * into interface blocks without an instance name. + * + * For example, the following shader: + * + * out block { + * float block_var; + * } inst_name; + * + * main() + * { + * inst_name.block_var = 0.0; + * } + * + * Is rewritten to: + * + * out block { + * float block_var; + * }; + * + * main() + * { + * block_var = 0.0; + * } + * + * This takes place after the shader code has already been verified with + * the interface name in place. + * + * The linking phase will use the interface block name rather than the + * interface's instance name when linking interfaces. + * + * This modification to the ir allows our currently existing dead code + * elimination to work with interface blocks without changes. + */ + +#include "glsl_symbol_table.h" +#include "ir.h" +#include "ir_optimization.h" +#include "ir_rvalue_visitor.h" +#include "program/hash_table.h" + +static const glsl_type * +process_array_type(const glsl_type *type, unsigned idx) +{ + const glsl_type *element_type = type->fields.array; + if (element_type->is_array()) { + const glsl_type *new_array_type = process_array_type(element_type, idx); + return glsl_type::get_array_instance(new_array_type, type->length); + } else { + return glsl_type::get_array_instance( + element_type->fields.structure[idx].type, type->length); + } +} + +static ir_rvalue * +process_array_ir(void * const mem_ctx, + ir_dereference_array *deref_array_prev, + ir_rvalue *deref_var) +{ + ir_dereference_array *deref_array = + deref_array_prev->array->as_dereference_array(); + + if (deref_array == NULL) { + return new(mem_ctx) ir_dereference_array(deref_var, + deref_array_prev->array_index); + } else { + deref_array = (ir_dereference_array *) process_array_ir(mem_ctx, + deref_array, + deref_var); + return new(mem_ctx) ir_dereference_array(deref_array, + deref_array_prev->array_index); + } +} + +namespace { + +class flatten_named_interface_blocks_declarations : public ir_rvalue_visitor +{ +public: + void * const mem_ctx; + hash_table *interface_namespace; + + flatten_named_interface_blocks_declarations(void *mem_ctx) + : mem_ctx(mem_ctx), + interface_namespace(NULL) + { + } + + void run(exec_list *instructions); + + virtual ir_visitor_status visit_leave(ir_assignment *); + virtual void handle_rvalue(ir_rvalue **rvalue); +}; + +} /* anonymous namespace */ + +void +flatten_named_interface_blocks_declarations::run(exec_list *instructions) +{ + interface_namespace = hash_table_ctor(0, hash_table_string_hash, + hash_table_string_compare); + + /* First pass: adjust instance block variables with an instance name + * to not have an instance name. + * + * The interface block variables are stored in the interface_namespace + * hash table so they can be used in the second pass. + */ + foreach_in_list_safe(ir_instruction, node, instructions) { + ir_variable *var = node->as_variable(); + if (!var || !var->is_interface_instance()) + continue; + + /* It should be possible to handle uniforms during this pass, + * but, this will require changes to the other uniform block + * support code. + */ + if (var->data.mode == ir_var_uniform || + var->data.mode == ir_var_shader_storage) + continue; + + const glsl_type * iface_t = var->type->without_array(); + exec_node *insert_pos = var; + + assert (iface_t->is_interface()); + + for (unsigned i = 0; i < iface_t->length; i++) { + const char * field_name = iface_t->fields.structure[i].name; + char *iface_field_name = + ralloc_asprintf(mem_ctx, "%s %s.%s.%s", + var->data.mode == ir_var_shader_in ? "in" : "out", + iface_t->name, var->name, field_name); + + ir_variable *found_var = + (ir_variable *) hash_table_find(interface_namespace, + iface_field_name); + if (!found_var) { + ir_variable *new_var; + char *var_name = + ralloc_strdup(mem_ctx, iface_t->fields.structure[i].name); + if (!var->type->is_array()) { + new_var = + new(mem_ctx) ir_variable(iface_t->fields.structure[i].type, + var_name, + (ir_variable_mode) var->data.mode); + new_var->data.from_named_ifc_block_nonarray = 1; + } else { + const glsl_type *new_array_type = + process_array_type(var->type, i); + new_var = + new(mem_ctx) ir_variable(new_array_type, + var_name, + (ir_variable_mode) var->data.mode); + new_var->data.from_named_ifc_block_array = 1; + } + new_var->data.location = iface_t->fields.structure[i].location; + new_var->data.explicit_location = (new_var->data.location >= 0); + new_var->data.interpolation = + iface_t->fields.structure[i].interpolation; + new_var->data.centroid = iface_t->fields.structure[i].centroid; + new_var->data.sample = iface_t->fields.structure[i].sample; + new_var->data.patch = iface_t->fields.structure[i].patch; + new_var->data.stream = var->data.stream; + new_var->data.how_declared = var->data.how_declared; + + new_var->init_interface_type(iface_t); + hash_table_insert(interface_namespace, new_var, + iface_field_name); + insert_pos->insert_after(new_var); + insert_pos = new_var; + } + } + var->remove(); + } + + /* Second pass: visit all ir_dereference_record instances, and if they + * reference an interface block, then flatten the refererence out. + */ + visit_list_elements(this, instructions); + hash_table_dtor(interface_namespace); + interface_namespace = NULL; +} + +ir_visitor_status +flatten_named_interface_blocks_declarations::visit_leave(ir_assignment *ir) +{ + ir_dereference_record *lhs_rec = ir->lhs->as_dereference_record(); + if (lhs_rec) { + ir_rvalue *lhs_rec_tmp = lhs_rec; + handle_rvalue(&lhs_rec_tmp); + if (lhs_rec_tmp != lhs_rec) { + ir->set_lhs(lhs_rec_tmp); + } + } + return rvalue_visit(ir); +} + +void +flatten_named_interface_blocks_declarations::handle_rvalue(ir_rvalue **rvalue) +{ + if (*rvalue == NULL) + return; + + ir_dereference_record *ir = (*rvalue)->as_dereference_record(); + if (ir == NULL) + return; + + ir_variable *var = ir->variable_referenced(); + if (var == NULL) + return; + + if (!var->is_interface_instance()) + return; + + /* It should be possible to handle uniforms during this pass, + * but, this will require changes to the other uniform block + * support code. + */ + if (var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage) + return; + + if (var->get_interface_type() != NULL) { + char *iface_field_name = + ralloc_asprintf(mem_ctx, "%s %s.%s.%s", + var->data.mode == ir_var_shader_in ? "in" : "out", + var->get_interface_type()->name, + var->name, ir->field); + /* Find the variable in the set of flattened interface blocks */ + ir_variable *found_var = + (ir_variable *) hash_table_find(interface_namespace, + iface_field_name); + assert(found_var); + + ir_dereference_variable *deref_var = + new(mem_ctx) ir_dereference_variable(found_var); + + ir_dereference_array *deref_array = + ir->record->as_dereference_array(); + if (deref_array != NULL) { + *rvalue = process_array_ir(mem_ctx, deref_array, + (ir_rvalue *)deref_var); + } else { + *rvalue = deref_var; + } + } +} + +void +lower_named_interface_blocks(void *mem_ctx, gl_shader *shader) +{ + flatten_named_interface_blocks_declarations v_decl(mem_ctx); + v_decl.run(shader->ir); +} + |