X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flink_interface_blocks.cpp;h=0ce502d4fdde25f5fc2dff0be6de6f3c33a7178a;hb=6305caea521fb2c7f82100db490f03d279185c43;hp=4f67291d85fd32a9a73cfe2651fa21fcbde18dfd;hpb=659ec1c958b59b77b5334d1121722ea0c80dddf8;p=mesa.git diff --git a/src/glsl/link_interface_blocks.cpp b/src/glsl/link_interface_blocks.cpp index 4f67291d85f..0ce502d4fdd 100644 --- a/src/glsl/link_interface_blocks.cpp +++ b/src/glsl/link_interface_blocks.cpp @@ -30,84 +30,356 @@ #include "glsl_symbol_table.h" #include "linker.h" #include "main/macros.h" +#include "program/hash_table.h" + +namespace { + +/** + * Information about a single interface block definition that we need to keep + * track of in order to check linkage rules. + * + * Note: this class is expected to be short lived, so it doesn't make copies + * of the strings it references; it simply borrows the pointers from the + * ir_variable class. + */ +struct interface_block_definition +{ + /** + * Extract an interface block definition from an ir_variable that + * represents either the interface instance (for named interfaces), or a + * member of the interface (for unnamed interfaces). + */ + explicit interface_block_definition(const ir_variable *var) + : type(var->get_interface_type()), + instance_name(NULL), + array_size(-1) + { + if (var->is_interface_instance()) { + instance_name = var->name; + if (var->type->is_array()) + array_size = var->type->length; + } + explicitly_declared = (var->data.how_declared != ir_var_declared_implicitly); + } + + /** + * Interface block type + */ + const glsl_type *type; + + /** + * For a named interface block, the instance name. Otherwise NULL. + */ + const char *instance_name; + + /** + * For an interface block array, the array size (or 0 if unsized). + * Otherwise -1. + */ + int array_size; + + /** + * True if this interface block was explicitly declared in the shader; + * false if it was an implicitly declared built-in interface block. + */ + bool explicitly_declared; +}; + + +/** + * Check if two interfaces match, according to intrastage interface matching + * rules. If they do, and the first interface uses an unsized array, it will + * be updated to reflect the array size declared in the second interface. + */ bool +intrastage_match(interface_block_definition *a, + const interface_block_definition *b, + ir_variable_mode mode) +{ + /* Types must match. */ + if (a->type != b->type) { + /* Exception: if both the interface blocks are implicitly declared, + * don't force their types to match. They might mismatch due to the two + * shaders using different GLSL versions, and that's ok. + */ + if (a->explicitly_declared || b->explicitly_declared) + return false; + } + + /* Presence/absence of interface names must match. */ + if ((a->instance_name == NULL) != (b->instance_name == NULL)) + return false; + + /* For uniforms, instance names need not match. For shader ins/outs, + * it's not clear from the spec whether they need to match, but + * Mesa's implementation relies on them matching. + */ + if (a->instance_name != NULL && mode != ir_var_uniform && + strcmp(a->instance_name, b->instance_name) != 0) { + return false; + } + + /* Array vs. nonarray must be consistent, and sizes must be + * consistent, with the exception that unsized arrays match sized + * arrays. + */ + if ((a->array_size == -1) != (b->array_size == -1)) + return false; + if (b->array_size != 0) { + if (a->array_size == 0) + a->array_size = b->array_size; + else if (a->array_size != b->array_size) + return false; + } + + return true; +} + + +/** + * Check if two interfaces match, according to interstage (in/out) interface + * matching rules. + * + * If \c extra_array_level is true, then vertex-to-geometry shader matching + * rules are enforced (i.e. a successful match requires the consumer interface + * to be an array and the producer interface to be a non-array). + */ +bool +interstage_match(const interface_block_definition *producer, + const interface_block_definition *consumer, + bool extra_array_level) +{ + /* Unsized arrays should not occur during interstage linking. They + * should have all been assigned a size by link_intrastage_shaders. + */ + assert(consumer->array_size != 0); + assert(producer->array_size != 0); + + /* Types must match. */ + if (consumer->type != producer->type) { + /* Exception: if both the interface blocks are implicitly declared, + * don't force their types to match. They might mismatch due to the two + * shaders using different GLSL versions, and that's ok. + */ + if (consumer->explicitly_declared || producer->explicitly_declared) + return false; + } + if (extra_array_level) { + /* Consumer must be an array, and producer must not. */ + if (consumer->array_size == -1) + return false; + if (producer->array_size != -1) + return false; + } else { + /* Array vs. nonarray must be consistent, and sizes must be consistent. + * Since unsized arrays have been ruled out, we can check this by just + * making sure the sizes are equal. + */ + if (consumer->array_size != producer->array_size) + return false; + } + return true; +} + + +/** + * This class keeps track of a mapping from an interface block name to the + * necessary information about that interface block to determine whether to + * generate a link error. + * + * Note: this class is expected to be short lived, so it doesn't make copies + * of the strings it references; it simply borrows the pointers from the + * ir_variable class. + */ +class interface_block_definitions +{ +public: + interface_block_definitions() + : mem_ctx(ralloc_context(NULL)), + ht(hash_table_ctor(0, hash_table_string_hash, + hash_table_string_compare)) + { + } + + ~interface_block_definitions() + { + hash_table_dtor(ht); + ralloc_free(mem_ctx); + } + + /** + * Lookup the interface definition having the given block name. Return + * NULL if none is found. + */ + interface_block_definition *lookup(const char *block_name) + { + return (interface_block_definition *) hash_table_find(ht, block_name); + } + + /** + * Add a new interface definition. + */ + void store(const interface_block_definition &def) + { + interface_block_definition *hash_entry = + rzalloc(mem_ctx, interface_block_definition); + *hash_entry = def; + hash_table_insert(ht, hash_entry, def.type->name); + } + +private: + /** + * Ralloc context for data structures allocated by this class. + */ + void *mem_ctx; + + /** + * Hash table mapping interface block name to an \c + * interface_block_definition struct. interface_block_definition structs + * are allocated using \c mem_ctx. + */ + hash_table *ht; +}; + + +}; /* anonymous namespace */ + + +void validate_intrastage_interface_blocks(struct gl_shader_program *prog, const gl_shader **shader_list, unsigned num_shaders) { - glsl_symbol_table interfaces; + interface_block_definitions in_interfaces; + interface_block_definitions out_interfaces; + interface_block_definitions uniform_interfaces; for (unsigned int i = 0; i < num_shaders; i++) { if (shader_list[i] == NULL) continue; - foreach_list(node, shader_list[i]->ir) { - ir_variable *var = ((ir_instruction *) node)->as_variable(); + foreach_in_list(ir_instruction, node, shader_list[i]->ir) { + ir_variable *var = node->as_variable(); if (!var) continue; - const glsl_type *iface_type = var->interface_type; + const glsl_type *iface_type = var->get_interface_type(); if (iface_type == NULL) continue; - const glsl_type *old_iface_type = - interfaces.get_interface(iface_type->name, - (enum ir_variable_mode) var->mode); + interface_block_definitions *definitions; + switch (var->data.mode) { + case ir_var_shader_in: + definitions = &in_interfaces; + break; + case ir_var_shader_out: + definitions = &out_interfaces; + break; + case ir_var_uniform: + definitions = &uniform_interfaces; + break; + default: + /* Only in, out, and uniform interfaces are legal, so we should + * never get here. + */ + assert(!"illegal interface type"); + continue; + } + + const interface_block_definition def(var); + interface_block_definition *prev_def = + definitions->lookup(iface_type->name); - if (old_iface_type == NULL) { + if (prev_def == NULL) { /* This is the first time we've seen the interface, so save - * it into our symbol table. + * it into the appropriate data structure. */ - interfaces.add_interface(iface_type->name, iface_type, - (enum ir_variable_mode) var->mode); - } else if (old_iface_type != iface_type) { + definitions->store(def); + } else if (!intrastage_match(prev_def, &def, + (ir_variable_mode) var->data.mode)) { linker_error(prog, "definitions of interface block `%s' do not" " match\n", iface_type->name); - return false; + return; } } } - - return true; } -bool -validate_interstage_interface_blocks(const gl_shader *producer, - const gl_shader *consumer) +void +validate_interstage_inout_blocks(struct gl_shader_program *prog, + const gl_shader *producer, + const gl_shader *consumer) { - glsl_symbol_table interfaces; + interface_block_definitions definitions; + const bool extra_array_level = consumer->Stage == MESA_SHADER_GEOMETRY; - /* Add non-output interfaces from the consumer to the symbol table. */ - foreach_list(node, consumer->ir) { - ir_variable *var = ((ir_instruction *) node)->as_variable(); - if (!var || !var->interface_type || var->mode == ir_var_shader_out) + /* Add input interfaces from the consumer to the symbol table. */ + foreach_in_list(ir_instruction, node, consumer->ir) { + ir_variable *var = node->as_variable(); + if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_in) continue; - interfaces.add_interface(var->interface_type->name, - var->interface_type, - (enum ir_variable_mode) var->mode); + definitions.store(interface_block_definition(var)); } - /* Verify that the producer's interfaces match. */ - foreach_list(node, producer->ir) { - ir_variable *var = ((ir_instruction *) node)->as_variable(); - if (!var || !var->interface_type || var->mode == ir_var_shader_in) + /* Verify that the producer's output interfaces match. */ + foreach_in_list(ir_instruction, node, producer->ir) { + ir_variable *var = node->as_variable(); + if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_out) continue; - enum ir_variable_mode consumer_mode = - var->mode == ir_var_uniform ? ir_var_uniform : ir_var_shader_in; - const glsl_type *expected_type = - interfaces.get_interface(var->interface_type->name, consumer_mode); + interface_block_definition *consumer_def = + definitions.lookup(var->get_interface_type()->name); /* The consumer doesn't use this output block. Ignore it. */ - if (expected_type == NULL) + if (consumer_def == NULL) continue; - if (var->interface_type != expected_type) - return false; + const interface_block_definition producer_def(var); + + if (!interstage_match(&producer_def, consumer_def, extra_array_level)) { + linker_error(prog, "definitions of interface block `%s' do not " + "match\n", var->get_interface_type()->name); + return; + } } +} - return true; + +void +validate_interstage_uniform_blocks(struct gl_shader_program *prog, + gl_shader **stages, int num_stages) +{ + interface_block_definitions definitions; + + for (int i = 0; i < num_stages; i++) { + if (stages[i] == NULL) + continue; + + const gl_shader *stage = stages[i]; + foreach_in_list(ir_instruction, node, stage->ir) { + ir_variable *var = node->as_variable(); + if (!var || !var->get_interface_type() || var->data.mode != ir_var_uniform) + continue; + + interface_block_definition *old_def = + definitions.lookup(var->get_interface_type()->name); + const interface_block_definition new_def(var); + if (old_def == NULL) { + definitions.store(new_def); + } else { + /* Interstage uniform matching rules are the same as intrastage + * uniform matchin rules (for uniforms, it is as though all + * shaders are in the same shader stage). + */ + if (!intrastage_match(old_def, &new_def, ir_var_uniform)) { + linker_error(prog, "definitions of interface block `%s' do not " + "match\n", var->get_interface_type()->name); + return; + } + } + } + } }