X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fcompiler%2Fglsl%2Flink_interface_blocks.cpp;h=4471b41cf0e9a0fbc36d33a07e4db474456983a2;hb=95f555a93a8891ebba2a291eecd984eb2364d636;hp=4c6fb56f8912447949221a5f9c36151582c4b8f4;hpb=7f6a0cb29c89a03441be744680a2145445be3a3c;p=mesa.git diff --git a/src/compiler/glsl/link_interface_blocks.cpp b/src/compiler/glsl/link_interface_blocks.cpp index 4c6fb56f891..4471b41cf0e 100644 --- a/src/compiler/glsl/link_interface_blocks.cpp +++ b/src/compiler/glsl/link_interface_blocks.cpp @@ -30,57 +30,13 @@ #include "glsl_symbol_table.h" #include "linker.h" #include "main/macros.h" +#include "main/mtypes.h" #include "util/hash_table.h" +#include "util/u_string.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; -} - /** * Return true if interface members mismatch and its not allowed by GLSL. */ @@ -110,7 +66,7 @@ interstage_member_mismatch(struct gl_shader_program *prog, * interpolation qualifiers of variables of the same name do not * match." */ - if (prog->IsES || prog->Version < 440) + if (prog->IsES || prog->data->Version < 440) if (c->fields.structure[i].interpolation != p->fields.structure[i].interpolation) return true; @@ -129,7 +85,7 @@ interstage_member_mismatch(struct gl_shader_program *prog, * The table in Section 9.2.1 Linked Shaders of the GLSL ES 3.2 spec * says that sample need not match for varyings. */ - if (!prog->IsES || prog->Version < 310) + if (!prog->IsES || prog->data->Version < 310) if (c->fields.structure[i].centroid != p->fields.structure[i].centroid) return true; @@ -142,6 +98,60 @@ interstage_member_mismatch(struct gl_shader_program *prog, return false; } +/** + * 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, + bool match_precision) +{ + /* 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) && + (!prog->IsES || + interstage_member_mismatch(prog, a->get_interface_type(), + b->get_interface_type()))) + 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; + } + + bool type_match = (match_precision ? + a->type == b->type : + a->type->compare_no_precision(b->type)); + + /* If a block is an array then it must match across the shader. + * Unsized arrays are also processed and matched agaist sized arrays. + */ + if (!type_match && (b->type->is_array() || a->type->is_array()) && + (b->is_interface_instance() || a->is_interface_instance()) && + !validate_intrastage_arrays(prog, b, a, match_precision)) + return false; + + return true; +} + /** * Check if two interfaces match, according to interstage (in/out) interface * matching rules. @@ -154,12 +164,6 @@ static bool interstage_match(struct gl_shader_program *prog, 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, @@ -216,7 +220,7 @@ class interface_block_definitions public: interface_block_definitions() : mem_ctx(ralloc_context(NULL)), - ht(_mesa_hash_table_create(NULL, _mesa_key_hash_string, + ht(_mesa_hash_table_create(NULL, _mesa_hash_string, _mesa_key_string_equal)) { } @@ -242,7 +246,8 @@ public: return entry ? (ir_variable *) entry->data : NULL; } else { const struct hash_entry *entry = - _mesa_hash_table_search(ht, var->get_interface_type()->name); + _mesa_hash_table_search(ht, + var->get_interface_type()->without_array()->name); return entry ? (ir_variable *) entry->data : NULL; } } @@ -263,7 +268,8 @@ public: 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); + _mesa_hash_table_insert(ht, + var->get_interface_type()->without_array()->name, var); } } @@ -336,7 +342,8 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog, * it into the appropriate data structure. */ definitions->store(var); - } else if (!intrastage_match(prev_def, var, prog)) { + } else if (!intrastage_match(prev_def, var, prog, + true /* match_precision */)) { linker_error(prog, "definitions of interface block `%s' do not" " match\n", iface_type->name); return; @@ -345,10 +352,19 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog, } } +static bool +is_builtin_gl_in_block(ir_variable *var, int consumer_stage) +{ + return !strcmp(var->name, "gl_in") && + (consumer_stage == MESA_SHADER_TESS_CTRL || + consumer_stage == MESA_SHADER_TESS_EVAL || + consumer_stage == MESA_SHADER_GEOMETRY); +} + void validate_interstage_inout_blocks(struct gl_shader_program *prog, - const gl_shader *producer, - const gl_shader *consumer) + const gl_linked_shader *producer, + const gl_linked_shader *consumer) { interface_block_definitions definitions; /* VS -> GS, VS -> TCS, VS -> TES, TES -> GS */ @@ -356,28 +372,127 @@ validate_interstage_inout_blocks(struct gl_shader_program *prog, 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) { + /* Check that block re-declarations of gl_PerVertex are compatible + * across shaders: From OpenGL Shading Language 4.5, section + * "7.1 Built-In Language Variables", page 130 of the PDF: + * + * "If multiple shaders using members of a built-in block belonging + * to the same interface are linked together in the same program, + * they must all redeclare the built-in block in the same way, as + * described in section 4.3.9 “Interface Blocks” for interface-block + * matching, or a link-time error will result." + * + * This is done explicitly outside of iterating the member variable + * declarations because it is possible that the variables are not used and + * so they would have been optimised out. + */ + const glsl_type *consumer_iface = + consumer->symbols->get_interface("gl_PerVertex", + ir_var_shader_in); + + const glsl_type *producer_iface = + producer->symbols->get_interface("gl_PerVertex", + ir_var_shader_out); + + if (producer_iface && consumer_iface && + interstage_member_mismatch(prog, consumer_iface, producer_iface)) { + linker_error(prog, "Incompatible or missing gl_PerVertex re-declaration " + "in consecutive shaders"); + return; + } + + /* Desktop OpenGL requires redeclaration of the built-in interfaces for + * SSO programs. Passes above implement following rules: + * + * From Section 7.4 (Program Pipeline Objects) of the OpenGL 4.6 Core + * spec: + * + * "To use any built-in input or output in the gl_PerVertex and + * gl_PerFragment blocks in separable program objects, shader code + * must redeclare those blocks prior to use. A separable program + * will fail to link if: + * + * it contains multiple shaders of a single type with different + * redeclarations of these built-in input and output blocks; or + * + * any shader uses a built-in block member not found in the + * redeclaration of that block." + * + * ARB_separate_shader_objects issues section (issue #28) states that + * redeclaration is not required for GLSL shaders using #version 140 or + * earlier (since interface blocks are not possible with older versions). + * + * From Section 7.4.1 (Shader Interface Matching) of the OpenGL ES 3.1 + * spec: + * + * "Built-in inputs or outputs do not affect interface matching." + * + * GL_OES_shader_io_blocks adds following: + * + * "When using any built-in input or output in the gl_PerVertex block + * in separable program objects, shader code may redeclare that block + * prior to use. If the shader does not redeclare the block, the + * intrinsically declared definition of that block will be used." + */ + + /* Add output interfaces from the producer to the symbol table. */ + 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_in) + if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_out) continue; + /* Built-in interface redeclaration check. */ + if (prog->SeparateShader && !prog->IsES && prog->data->Version >= 150 && + var->data.how_declared == ir_var_declared_implicitly && + var->data.used && !producer_iface) { + linker_error(prog, "missing output builtin block %s redeclaration " + "in separable shader program", + var->get_interface_type()->name); + return; + } + definitions.store(var); } - /* Verify that the producer's output interfaces match. */ - foreach_in_list(ir_instruction, node, producer->ir) { + /* Verify that the consumer's input interfaces match. */ + 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_out) + if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_in) continue; - ir_variable *consumer_def = definitions.lookup(var); + ir_variable *producer_def = definitions.lookup(var); - /* The consumer doesn't use this output block. Ignore it. */ - if (consumer_def == NULL) - continue; + /* Built-in interface redeclaration check. */ + if (prog->SeparateShader && !prog->IsES && prog->data->Version >= 150 && + var->data.how_declared == ir_var_declared_implicitly && + var->data.used && !producer_iface) { + linker_error(prog, "missing input builtin block %s redeclaration " + "in separable shader program", + var->get_interface_type()->name); + return; + } + + /* The producer doesn't generate this input: fail to link. Skip built-in + * 'gl_in[]' since that may not be present if the producer does not + * write to any of the pre-defined outputs (e.g. if the vertex shader + * does not write to gl_Position, etc), which is allowed and results in + * undefined behavior. + * + * From Section 4.3.4 (Inputs) of the GLSL 1.50 spec: + * + * "Only the input variables that are actually read need to be written + * by the previous stage; it is allowed to have superfluous + * declarations of input variables." + */ + if (producer_def == NULL && + !is_builtin_gl_in_block(var, consumer->Stage) && var->data.used) { + linker_error(prog, "Input block `%s' is not an output of " + "the previous stage\n", var->get_interface_type()->name); + return; + } - if (!interstage_match(prog, var, consumer_def, extra_array_level)) { + if (producer_def && + !interstage_match(prog, producer_def, var, extra_array_level)) { linker_error(prog, "definitions of interface block `%s' do not " "match\n", var->get_interface_type()->name); return; @@ -388,15 +503,15 @@ validate_interstage_inout_blocks(struct gl_shader_program *prog, void validate_interstage_uniform_blocks(struct gl_shader_program *prog, - gl_shader **stages, int num_stages) + gl_linked_shader **stages) { interface_block_definitions definitions; - for (int i = 0; i < num_stages; i++) { + for (int i = 0; i < MESA_SHADER_STAGES; i++) { if (stages[i] == NULL) continue; - const gl_shader *stage = stages[i]; + const gl_linked_shader *stage = stages[i]; foreach_in_list(ir_instruction, node, stage->ir) { ir_variable *var = node->as_variable(); if (!var || !var->get_interface_type() || @@ -412,8 +527,8 @@ validate_interstage_uniform_blocks(struct gl_shader_program *prog, * 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 " + if (!intrastage_match(old_def, var, prog, false /* precision */)) { + linker_error(prog, "definitions of uniform block `%s' do not " "match\n", var->get_interface_type()->name); return; }