diff options
Diffstat (limited to 'src/compiler/glsl/link_interface_blocks.cpp')
-rw-r--r-- | src/compiler/glsl/link_interface_blocks.cpp | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/src/compiler/glsl/link_interface_blocks.cpp b/src/compiler/glsl/link_interface_blocks.cpp new file mode 100644 index 00000000000..64c30fea9a3 --- /dev/null +++ b/src/compiler/glsl/link_interface_blocks.cpp @@ -0,0 +1,357 @@ +/* + * Copyright © 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 link_interface_blocks.cpp + * Linker support for GLSL's interface blocks. + */ + +#include "ir.h" +#include "glsl_symbol_table.h" +#include "linker.h" +#include "main/macros.h" +#include "util/hash_table.h" + + +namespace { + +/** + * 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(ir_variable *a, + ir_variable *b, + struct gl_shader_program *prog) +{ + /* Types must match. */ + if (a->get_interface_type() != b->get_interface_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->data.how_declared != ir_var_declared_implicitly || + b->data.how_declared != ir_var_declared_implicitly) + return false; + } + + /* Presence/absence of interface names must match. */ + if (a->is_interface_instance() != b->is_interface_instance()) + 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->is_interface_instance() && b->data.mode != ir_var_uniform && + b->data.mode != ir_var_shader_storage && + strcmp(a->name, b->name) != 0) { + return false; + } + + /* If a block is an array then it must match across the shader. + * Unsized arrays are also processed and matched agaist sized arrays. + */ + if (b->type != a->type && + (b->is_interface_instance() || a->is_interface_instance()) && + !validate_intrastage_arrays(prog, b, a)) + return false; + + return true; +} + + +/** + * Check if two interfaces match, according to interstage (in/out) interface + * matching rules. + * + * If \c extra_array_level is true, the consumer interface is required to be + * an array and the producer interface is required to be a non-array. + * This is used for tessellation control and geometry shader consumers. + */ +bool +interstage_match(ir_variable *producer, + ir_variable *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->type->is_unsized_array()); + assert(!producer->type->is_unsized_array()); + + /* Types must match. */ + if (consumer->get_interface_type() != producer->get_interface_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->data.how_declared != ir_var_declared_implicitly || + producer->data.how_declared != ir_var_declared_implicitly) + return false; + } + + /* Ignore outermost array if geom shader */ + const glsl_type *consumer_instance_type; + if (extra_array_level) { + consumer_instance_type = consumer->type->fields.array; + } else { + consumer_instance_type = consumer->type; + } + + /* If a block is an array then it must match across shaders. + * Since unsized arrays have been ruled out, we can check this by just + * making sure the types are equal. + */ + if ((consumer->is_interface_instance() && + consumer_instance_type->is_array()) || + (producer->is_interface_instance() && + producer->type->is_array())) { + if (consumer_instance_type != producer->type) + 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(_mesa_hash_table_create(NULL, _mesa_key_hash_string, + _mesa_key_string_equal)) + { + } + + ~interface_block_definitions() + { + ralloc_free(mem_ctx); + _mesa_hash_table_destroy(ht, NULL); + } + + /** + * Lookup the interface definition. Return NULL if none is found. + */ + ir_variable *lookup(ir_variable *var) + { + if (var->data.explicit_location && + var->data.location >= VARYING_SLOT_VAR0) { + char location_str[11]; + snprintf(location_str, 11, "%d", var->data.location); + + const struct hash_entry *entry = + _mesa_hash_table_search(ht, location_str); + return entry ? (ir_variable *) entry->data : NULL; + } else { + const struct hash_entry *entry = + _mesa_hash_table_search(ht, var->get_interface_type()->name); + return entry ? (ir_variable *) entry->data : NULL; + } + } + + /** + * Add a new interface definition. + */ + void store(ir_variable *var) + { + if (var->data.explicit_location && + var->data.location >= VARYING_SLOT_VAR0) { + /* If explicit location is given then lookup the variable by location. + * We turn the location into a string and use this as the hash key + * rather than the name. Note: We allocate enough space for a 32-bit + * unsigned location value which is overkill but future proof. + */ + char location_str[11]; + snprintf(location_str, 11, "%d", var->data.location); + _mesa_hash_table_insert(ht, ralloc_strdup(mem_ctx, location_str), var); + } else { + _mesa_hash_table_insert(ht, var->get_interface_type()->name, var); + } + } + +private: + /** + * Ralloc context for data structures allocated by this class. + */ + void *mem_ctx; + + /** + * Hash table mapping interface block name to an \c + * ir_variable. + */ + hash_table *ht; +}; + + +}; /* anonymous namespace */ + + +void +validate_intrastage_interface_blocks(struct gl_shader_program *prog, + const gl_shader **shader_list, + unsigned num_shaders) +{ + interface_block_definitions in_interfaces; + interface_block_definitions out_interfaces; + interface_block_definitions uniform_interfaces; + interface_block_definitions buffer_interfaces; + + for (unsigned int i = 0; i < num_shaders; i++) { + if (shader_list[i] == NULL) + continue; + + 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->get_interface_type(); + + if (iface_type == NULL) + continue; + + 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; + case ir_var_shader_storage: + definitions = &buffer_interfaces; + break; + default: + /* Only in, out, and uniform interfaces are legal, so we should + * never get here. + */ + assert(!"illegal interface type"); + continue; + } + + ir_variable *prev_def = definitions->lookup(var); + if (prev_def == NULL) { + /* This is the first time we've seen the interface, so save + * it into the appropriate data structure. + */ + definitions->store(var); + } else if (!intrastage_match(prev_def, var, prog)) { + linker_error(prog, "definitions of interface block `%s' do not" + " match\n", iface_type->name); + return; + } + } + } +} + +void +validate_interstage_inout_blocks(struct gl_shader_program *prog, + const gl_shader *producer, + const gl_shader *consumer) +{ + interface_block_definitions definitions; + /* VS -> GS, VS -> TCS, VS -> TES, TES -> GS */ + const bool extra_array_level = (producer->Stage == MESA_SHADER_VERTEX && + consumer->Stage != MESA_SHADER_FRAGMENT) || + consumer->Stage == MESA_SHADER_GEOMETRY; + + /* 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; + + definitions.store(var); + } + + /* 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; + + ir_variable *consumer_def = definitions.lookup(var); + + /* The consumer doesn't use this output block. Ignore it. */ + if (consumer_def == NULL) + continue; + + if (!interstage_match(var, consumer_def, extra_array_level)) { + linker_error(prog, "definitions of interface block `%s' do not " + "match\n", var->get_interface_type()->name); + return; + } + } +} + + +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 && + var->data.mode != ir_var_shader_storage)) + continue; + + ir_variable *old_def = definitions.lookup(var); + if (old_def == NULL) { + definitions.store(var); + } 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, var, prog)) { + linker_error(prog, "definitions of interface block `%s' do not " + "match\n", var->get_interface_type()->name); + return; + } + } + } + } +} |