From 15e05b999b779dc48a8e768184b9c69e859c203b Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Wed, 25 Sep 2013 14:07:37 -0700 Subject: [PATCH] glsl: Modify array_sizing_visitor to handle unnamed interface blocks. We were already setting the array size of unsized arrays that appeared inside unnamed interface blocks, but we weren't updating ir_variable::interface_type to reflect the new array size, causing bogus link errors. This patch causes array_sizing_visitor to keep track of all the unnamed interface types it sees, and the ir_variables corresponding to each one. After the visitor runs, a new function, fixup_unnamed_interface_types(), adjusts each unnamed interface type to correctly correspond with the array sizes in the ir_variables. Fixes piglit tests: - spec/glsl-1.50/execution/unsized-in-unnamed-interface-block-gs - spec/glsl-1.50/execution/unsized-in-unnamed-interface-block-multiple Reviewed-by: Jordan Justen --- src/glsl/ir.h | 4 +-- src/glsl/linker.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/glsl/ir.h b/src/glsl/ir.h index 2a0afada1ea..0f8e9a15d96 100644 --- a/src/glsl/ir.h +++ b/src/glsl/ir.h @@ -406,8 +406,8 @@ public: /** * Change this->interface_type on a variable that previously had a - * different interface_type. This is used during linking to set the size - * of arrays in interface blocks. + * different, but compatible, interface_type. This is used during linking + * to set the size of arrays in interface blocks. */ void change_interface_type(const struct glsl_type *type) { diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index db1fe9a0d85..9095a401540 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -1033,6 +1033,19 @@ get_main_function_signature(gl_shader *sh) */ class array_sizing_visitor : public ir_hierarchical_visitor { public: + array_sizing_visitor() + : mem_ctx(ralloc_context(NULL)), + unnamed_interfaces(hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare)) + { + } + + ~array_sizing_visitor() + { + hash_table_dtor(this->unnamed_interfaces); + ralloc_free(this->mem_ctx); + } + virtual ir_visitor_status visit(ir_variable *var) { fixup_type(&var->type, var->max_array_access); @@ -1053,10 +1066,38 @@ public: var->type = glsl_type::get_array_instance(new_type, var->type->length); } + } else if (const glsl_type *ifc_type = var->get_interface_type()) { + /* Store a pointer to the variable in the unnamed_interfaces + * hashtable. + */ + ir_variable **interface_vars = (ir_variable **) + hash_table_find(this->unnamed_interfaces, ifc_type); + if (interface_vars == NULL) { + interface_vars = rzalloc_array(mem_ctx, ir_variable *, + ifc_type->length); + hash_table_insert(this->unnamed_interfaces, interface_vars, + ifc_type); + } + unsigned index = ifc_type->field_index(var->name); + assert(index < ifc_type->length); + assert(interface_vars[index] == NULL); + interface_vars[index] = var; } return visit_continue; } + /** + * For each unnamed interface block that was discovered while running the + * visitor, adjust the interface type to reflect the newly assigned array + * sizes, and fix up the ir_variable nodes to point to the new interface + * type. + */ + void fixup_unnamed_interface_types() + { + hash_table_call_foreach(this->unnamed_interfaces, + fixup_unnamed_interface_type, NULL); + } + private: /** * If the type pointed to by \c type represents an unsized array, replace @@ -1109,6 +1150,50 @@ private: delete [] fields; return new_ifc_type; } + + static void fixup_unnamed_interface_type(const void *key, void *data, + void *) + { + const glsl_type *ifc_type = (const glsl_type *) key; + ir_variable **interface_vars = (ir_variable **) data; + unsigned num_fields = ifc_type->length; + glsl_struct_field *fields = new glsl_struct_field[num_fields]; + memcpy(fields, ifc_type->fields.structure, + num_fields * sizeof(*fields)); + bool interface_type_changed = false; + for (unsigned i = 0; i < num_fields; i++) { + if (interface_vars[i] != NULL && + fields[i].type != interface_vars[i]->type) { + fields[i].type = interface_vars[i]->type; + interface_type_changed = true; + } + } + if (!interface_type_changed) { + delete [] fields; + return; + } + glsl_interface_packing packing = + (glsl_interface_packing) ifc_type->interface_packing; + const glsl_type *new_ifc_type = + glsl_type::get_interface_instance(fields, num_fields, packing, + ifc_type->name); + delete [] fields; + for (unsigned i = 0; i < num_fields; i++) { + if (interface_vars[i] != NULL) + interface_vars[i]->change_interface_type(new_ifc_type); + } + } + + /** + * Memory context used to allocate the data in \c unnamed_interfaces. + */ + void *mem_ctx; + + /** + * Hash table from const glsl_type * to an array of ir_variable *'s + * pointing to the ir_variables constituting each unnamed interface block. + */ + hash_table *unnamed_interfaces; }; /** @@ -1383,6 +1468,7 @@ link_intrastage_shaders(void *mem_ctx, */ array_sizing_visitor v; v.run(linked->ir); + v.fixup_unnamed_interface_types(); return linked; } -- 2.30.2