diff --git a/src/compiler/glsl/gl_nir_link_varyings.c b/src/compiler/glsl/gl_nir_link_varyings.c index db33882406c..e3ba6fb2812 100644 --- a/src/compiler/glsl/gl_nir_link_varyings.c +++ b/src/compiler/glsl/gl_nir_link_varyings.c @@ -31,6 +31,7 @@ #include "main/macros.h" #include "main/menums.h" #include "main/mtypes.h" +#include "program/symbol_table.h" #include "util/hash_table.h" #include "util/u_math.h" #include "util/perf/cpu_trace.h" @@ -134,6 +135,235 @@ find_deref(nir_shader *shader, const char *name) return false; } +/** + * Validate the types and qualifiers of an output from one stage against the + * matching input to another stage. + */ +static void +cross_validate_types_and_qualifiers(const struct gl_constants *consts, + struct gl_shader_program *prog, + const nir_variable *input, + const nir_variable *output, + gl_shader_stage consumer_stage, + gl_shader_stage producer_stage) +{ + /* Check that the types match between stages. + */ + const struct glsl_type *type_to_match = input->type; + + /* 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; + if (extra_array_level) { + assert(glsl_type_is_array(type_to_match)); + type_to_match = glsl_get_array_element(type_to_match); + } + + if (type_to_match != output->type) { + if (glsl_type_is_struct(output->type)) { + /* Structures across shader stages can have different name + * and considered to match in type if and only if structure + * members match in name, type, qualification, and declaration + * order. The precision doesn’t need to match. + */ + if (!glsl_record_compare(output->type, type_to_match, + false, /* match_name */ + true, /* match_locations */ + false /* match_precision */)) { + linker_error(prog, + "%s shader output `%s' declared as struct `%s', " + "doesn't match in type with %s shader input " + "declared as struct `%s'\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + glsl_get_type_name(output->type), + _mesa_shader_stage_to_string(consumer_stage), + glsl_get_type_name(input->type)); + } + } else if (!glsl_type_is_array(output->type) || + !is_gl_identifier(output->name)) { + /* There is a bit of a special case for gl_TexCoord. This + * built-in is unsized by default. Applications that variable + * access it must redeclare it with a size. There is some + * language in the GLSL spec that implies the fragment shader + * and vertex shader do not have to agree on this size. Other + * driver behave this way, and one or two applications seem to + * rely on it. + * + * Neither declaration needs to be modified here because the array + * sizes are fixed later when update_array_sizes is called. + * + * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec: + * + * "Unlike user-defined varying variables, the built-in + * varying variables don't have a strict one-to-one + * correspondence between the vertex language and the + * fragment language." + */ + linker_error(prog, + "%s shader output `%s' declared as type `%s', " + "but %s shader input declared as type `%s'\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + glsl_get_type_name(output->type), + _mesa_shader_stage_to_string(consumer_stage), + glsl_get_type_name(input->type)); + return; + } + } + + /* Check that all of the qualifiers match between stages. + */ + + /* According to the OpenGL and OpenGLES GLSL specs, the centroid qualifier + * should match until OpenGL 4.3 and OpenGLES 3.1. The OpenGLES 3.0 + * conformance test suite does not verify that the qualifiers must match. + * The deqp test suite expects the opposite (OpenGLES 3.1) behavior for + * OpenGLES 3.0 drivers, so we relax the checking in all cases. + */ + if (false /* always skip the centroid check */ && + prog->GLSL_Version < (prog->IsES ? 310 : 430) && + input->data.centroid != output->data.centroid) { + linker_error(prog, + "%s shader output `%s' %s centroid qualifier, " + "but %s shader input %s centroid qualifier\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + (output->data.centroid) ? "has" : "lacks", + _mesa_shader_stage_to_string(consumer_stage), + (input->data.centroid) ? "has" : "lacks"); + return; + } + + if (input->data.sample != output->data.sample) { + linker_error(prog, + "%s shader output `%s' %s sample qualifier, " + "but %s shader input %s sample qualifier\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + (output->data.sample) ? "has" : "lacks", + _mesa_shader_stage_to_string(consumer_stage), + (input->data.sample) ? "has" : "lacks"); + return; + } + + if (input->data.patch != output->data.patch) { + linker_error(prog, + "%s shader output `%s' %s patch qualifier, " + "but %s shader input %s patch qualifier\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + (output->data.patch) ? "has" : "lacks", + _mesa_shader_stage_to_string(consumer_stage), + (input->data.patch) ? "has" : "lacks"); + return; + } + + /* The GLSL 4.20 and GLSL ES 3.00 specifications say: + * + * "As only outputs need be declared with invariant, an output from + * one shader stage will still match an input of a subsequent stage + * without the input being declared as invariant." + * + * while GLSL 4.10 says: + * + * "For variables leaving one shader and coming into another shader, + * the invariant keyword has to be used in both shaders, or a link + * error will result." + * + * and GLSL ES 1.00 section 4.6.4 "Invariance and Linking" says: + * + * "The invariance of varyings that are declared in both the vertex + * and fragment shaders must match." + */ + if (input->data.explicit_invariant != output->data.explicit_invariant && + prog->GLSL_Version < (prog->IsES ? 300 : 420)) { + linker_error(prog, + "%s shader output `%s' %s invariant qualifier, " + "but %s shader input %s invariant qualifier\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + (output->data.explicit_invariant) ? "has" : "lacks", + _mesa_shader_stage_to_string(consumer_stage), + (input->data.explicit_invariant) ? "has" : "lacks"); + return; + } + + /* GLSL >= 4.40 removes text requiring interpolation qualifiers + * to match cross stage, they must only match within the same stage. + * + * From page 84 (page 90 of the PDF) of the GLSL 4.40 spec: + * + * "It is a link-time error if, within the same stage, the interpolation + * qualifiers of variables of the same name do not match. + * + * Section 4.3.9 (Interpolation) of the GLSL ES 3.00 spec says: + * + * "When no interpolation qualifier is present, smooth interpolation + * is used." + * + * So we match variables where one is smooth and the other has no explicit + * qualifier. + */ + unsigned input_interpolation = input->data.interpolation; + unsigned output_interpolation = output->data.interpolation; + if (prog->IsES) { + if (input_interpolation == INTERP_MODE_NONE) + input_interpolation = INTERP_MODE_SMOOTH; + if (output_interpolation == INTERP_MODE_NONE) + output_interpolation = INTERP_MODE_SMOOTH; + } + if (input_interpolation != output_interpolation && + prog->GLSL_Version < 440) { + if (!consts->AllowGLSLCrossStageInterpolationMismatch) { + linker_error(prog, + "%s shader output `%s' specifies %s " + "interpolation qualifier, " + "but %s shader input specifies %s " + "interpolation qualifier\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + interpolation_string(output->data.interpolation), + _mesa_shader_stage_to_string(consumer_stage), + interpolation_string(input->data.interpolation)); + return; + } else { + linker_warning(prog, + "%s shader output `%s' specifies %s " + "interpolation qualifier, " + "but %s shader input specifies %s " + "interpolation qualifier\n", + _mesa_shader_stage_to_string(producer_stage), + output->name, + interpolation_string(output->data.interpolation), + _mesa_shader_stage_to_string(consumer_stage), + interpolation_string(input->data.interpolation)); + } + } +} + +/** + * Validate front and back color outputs against single color input + */ +static void +cross_validate_front_and_back_color(const struct gl_constants *consts, + struct gl_shader_program *prog, + const nir_variable *input, + const nir_variable *front_color, + const nir_variable *back_color, + gl_shader_stage consumer_stage, + gl_shader_stage producer_stage) +{ + if (front_color != NULL && front_color->data.assigned) + cross_validate_types_and_qualifiers(consts, prog, input, front_color, + consumer_stage, producer_stage); + + if (back_color != NULL && back_color->data.assigned) + cross_validate_types_and_qualifiers(consts, prog, input, back_color, + consumer_stage, producer_stage); +} + static unsigned compute_variable_location_slot(nir_variable *var, gl_shader_stage stage) { @@ -449,6 +679,200 @@ gl_nir_validate_first_and_last_interface_explicit_locations(const struct gl_cons } } +/** + * Check if we should force input / output matching between shader + * interfaces. + * + * Section 4.3.4 (Inputs) of the GLSL 4.10 specifications say: + * + * "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." + * + * However it's not defined anywhere as to how we should handle + * inputs that are not written in the previous stage and it's not + * clear what "actually read" means. + * + * The GLSL 4.20 spec however is much clearer: + * + * "Only the input variables that are statically read need to + * be written by the previous stage; it is allowed to have + * superfluous declarations of input variables." + * + * It also has a table that states it is an error to statically + * read an input that is not defined in the previous stage. While + * it is not an error to not statically write to the output (it + * just needs to be defined to not be an error). + * + * The text in the GLSL 4.20 spec was an attempt to clarify the + * previous spec iterations. However given the difference in spec + * and that some applications seem to depend on not erroring when + * the input is not actually read in control flow we only apply + * this rule to GLSL 4.20 and higher. GLSL 4.10 shaders have been + * seen in the wild that depend on the less strict interpretation. + */ +static bool +static_input_output_matching(struct gl_shader_program *prog) +{ + return prog->GLSL_Version >= (prog->IsES ? 0 : 420); +} + +/** + * Validate that outputs from one stage match inputs of another + */ +void +gl_nir_cross_validate_outputs_to_inputs(const struct gl_constants *consts, + struct gl_shader_program *prog, + struct gl_linked_shader *producer, + struct gl_linked_shader *consumer) +{ + struct _mesa_symbol_table *table = _mesa_symbol_table_ctor(); + struct explicit_location_info output_explicit_locations[MAX_VARYING][4] = {0}; + struct explicit_location_info input_explicit_locations[MAX_VARYING][4] = {0}; + + /* Find all shader outputs in the "producer" stage. + */ + nir_foreach_variable_with_modes(var, producer->Program->nir, nir_var_shader_out) { + if (!var->data.explicit_location + || var->data.location < VARYING_SLOT_VAR0) { + /* Interface block validation is handled elsewhere */ + if (!var->interface_type || is_gl_identifier(var->name)) + _mesa_symbol_table_add_symbol(table, var->name, var); + + } else { + /* User-defined varyings with explicit locations are handled + * differently because they do not need to have matching names. + */ + if (!validate_explicit_variable_location(consts, + output_explicit_locations, + var, prog, producer)) { + return; + } + } + } + + /* Find all shader inputs in the "consumer" stage. Any variables that have + * matching outputs already in the symbol table must have the same type and + * qualifiers. + * + * Exception: if the consumer is the geometry shader, then the inputs + * should be arrays and the type of the array element should match the type + * of the corresponding producer output. + */ + nir_foreach_variable_with_modes(input, consumer->Program->nir, nir_var_shader_in) { + if (strcmp(input->name, "gl_Color") == 0 && input->data.used) { + const nir_variable *front_color = + (nir_variable *) _mesa_symbol_table_find_symbol(table, "gl_FrontColor"); + + const nir_variable *back_color = + (nir_variable *) _mesa_symbol_table_find_symbol(table, "gl_BackColor"); + + cross_validate_front_and_back_color(consts, prog, input, + front_color, back_color, + consumer->Stage, producer->Stage); + } else if (strcmp(input->name, "gl_SecondaryColor") == 0 && input->data.used) { + const nir_variable *front_color = + (nir_variable *) _mesa_symbol_table_find_symbol(table, "gl_FrontSecondaryColor"); + + const nir_variable *back_color = + (nir_variable *) _mesa_symbol_table_find_symbol(table, "gl_BackSecondaryColor"); + + cross_validate_front_and_back_color(consts, prog, input, + front_color, back_color, + consumer->Stage, producer->Stage); + } else { + /* The rules for connecting inputs and outputs change in the presence + * of explicit locations. In this case, we no longer care about the + * names of the variables. Instead, we care only about the + * explicitly assigned location. + */ + nir_variable *output = NULL; + if (input->data.explicit_location + && input->data.location >= VARYING_SLOT_VAR0) { + + const struct glsl_type *type = + get_varying_type(input, consumer->Stage); + unsigned num_elements = glsl_count_attribute_slots(type, false); + unsigned idx = + compute_variable_location_slot(input, consumer->Stage); + unsigned slot_limit = idx + num_elements; + + if (!validate_explicit_variable_location(consts, + input_explicit_locations, + input, prog, consumer)) { + return; + } + + while (idx < slot_limit) { + if (idx >= MAX_VARYING) { + linker_error(prog, + "Invalid location %u in %s shader\n", idx, + _mesa_shader_stage_to_string(consumer->Stage)); + return; + } + + output = output_explicit_locations[idx][input->data.location_frac].var; + + if (output == NULL) { + /* A linker failure should only happen when there is no + * output declaration and there is Static Use of the + * declared input. + */ + if (input->data.used && static_input_output_matching(prog)) { + linker_error(prog, + "%s shader input `%s' with explicit location " + "has no matching output\n", + _mesa_shader_stage_to_string(consumer->Stage), + input->name); + break; + } + } else if (input->data.location != output->data.location) { + linker_error(prog, + "%s shader input `%s' with explicit location " + "has no matching output\n", + _mesa_shader_stage_to_string(consumer->Stage), + input->name); + break; + } + idx++; + } + } else { + /* Interface block validation is handled elsewhere */ + if (input->interface_type) + continue; + + output = (nir_variable *) + _mesa_symbol_table_find_symbol(table, input->name); + } + + if (output != NULL) { + /* Interface blocks have their own validation elsewhere so don't + * try validating them here. + */ + if (!(input->interface_type && output->interface_type)) + cross_validate_types_and_qualifiers(consts, prog, input, output, + consumer->Stage, + producer->Stage); + } else { + /* Check for input vars with unmatched output vars in prev stage + * taking into account that interface blocks could have a matching + * output but with different name, so we ignore them. + */ + assert(!input->data.assigned); + if (input->data.used && !input->interface_type && + !input->data.explicit_location && + static_input_output_matching(prog)) + linker_error(prog, + "%s shader input `%s' " + "has no matching output in the previous stage\n", + _mesa_shader_stage_to_string(consumer->Stage), + input->name); + } + } + } + + _mesa_symbol_table_dtor(table); +} /** * Assign locations for either VS inputs or FS outputs. diff --git a/src/compiler/glsl/gl_nir_link_varyings.h b/src/compiler/glsl/gl_nir_link_varyings.h index eef066f9472..e98c194fbef 100644 --- a/src/compiler/glsl/gl_nir_link_varyings.h +++ b/src/compiler/glsl/gl_nir_link_varyings.h @@ -40,6 +40,7 @@ struct gl_shader_program; struct gl_shader_stage; struct gl_shader; +struct gl_linked_shader; struct gl_type; @@ -225,4 +226,10 @@ gl_nir_validate_first_and_last_interface_explicit_locations(const struct gl_cons gl_shader_stage first_stage, gl_shader_stage last_stage); +void +gl_nir_cross_validate_outputs_to_inputs(const struct gl_constants *consts, + struct gl_shader_program *prog, + struct gl_linked_shader *producer, + struct gl_linked_shader *consumer); + #endif /* GLSL_LINK_VARYINGS_H */