summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Berry <[email protected]>2013-09-25 14:07:37 -0700
committerPaul Berry <[email protected]>2013-10-09 16:49:48 -0700
commit15e05b999b779dc48a8e768184b9c69e859c203b (patch)
treec671c33943fe7fc9f06e3873ac811fad05b9a64b /src
parent45e46b2e371e59f11f19b9169aa79ce2b1dffd6f (diff)
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 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/glsl/ir.h4
-rw-r--r--src/glsl/linker.cpp86
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;
}