X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flinker.cpp;h=49b6b8f4a5e84ece80fa5fcb3c3ec7e074e6ad9e;hb=2bb91274e2cc2290ce8e790335f1e57b81d9d783;hp=ba81c59ff2ca021c440113ef10a7c424bd2098cc;hpb=f8e6d19f3f40931be741b44d3edf210c38e13f0f;p=mesa.git diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index ba81c59ff2c..49b6b8f4a5e 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -101,7 +101,7 @@ public: virtual ir_visitor_status visit_enter(ir_call *ir) { - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); + exec_list_iterator sig_iter = ir->callee->parameters.iterator(); foreach_iter(exec_list_iterator, iter, *ir) { ir_rvalue *param_rval = (ir_rvalue *)iter.get(); ir_variable *sig_param = (ir_variable *)sig_iter.get(); @@ -117,6 +117,15 @@ public: sig_iter.next(); } + if (ir->return_deref != NULL) { + ir_variable *const var = ir->return_deref->variable_referenced(); + + if (strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } + } + return visit_continue_with_parent; } @@ -191,8 +200,8 @@ linker_warning(gl_shader_program *prog, const char *fmt, ...) void -invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, - int generic_base) +link_invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, + int generic_base) { foreach_list(node, sh->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); @@ -244,7 +253,10 @@ count_attribute_slots(const glsl_type *t) /** - * Verify that a vertex shader executable meets all semantic requirements + * Verify that a vertex shader executable meets all semantic requirements. + * + * Also sets prog->Vert.UsesClipDistance and prog->Vert.ClipDistanceArraySize + * as a side effect. * * \param shader Vertex shader executable to be verified */ @@ -255,11 +267,62 @@ validate_vertex_shader_executable(struct gl_shader_program *prog, if (shader == NULL) return true; - find_assignment_visitor find("gl_Position"); - find.run(shader->ir); - if (!find.variable_found()) { - linker_error(prog, "vertex shader does not write to `gl_Position'\n"); - return false; + /* From the GLSL 1.10 spec, page 48: + * + * "The variable gl_Position is available only in the vertex + * language and is intended for writing the homogeneous vertex + * position. All executions of a well-formed vertex shader + * executable must write a value into this variable. [...] The + * variable gl_Position is available only in the vertex + * language and is intended for writing the homogeneous vertex + * position. All executions of a well-formed vertex shader + * executable must write a value into this variable." + * + * while in GLSL 1.40 this text is changed to: + * + * "The variable gl_Position is available only in the vertex + * language and is intended for writing the homogeneous vertex + * position. It can be written at any time during shader + * execution. It may also be read back by a vertex shader + * after being written. This value will be used by primitive + * assembly, clipping, culling, and other fixed functionality + * operations, if present, that operate on primitives after + * vertex processing has occurred. Its value is undefined if + * the vertex shader executable does not write gl_Position." + */ + if (prog->Version < 140) { + find_assignment_visitor find("gl_Position"); + find.run(shader->ir); + if (!find.variable_found()) { + linker_error(prog, "vertex shader does not write to `gl_Position'\n"); + return false; + } + } + + prog->Vert.ClipDistanceArraySize = 0; + + if (prog->Version >= 130) { + /* From section 7.1 (Vertex Shader Special Variables) of the + * GLSL 1.30 spec: + * + * "It is an error for a shader to statically write both + * gl_ClipVertex and gl_ClipDistance." + */ + find_assignment_visitor clip_vertex("gl_ClipVertex"); + find_assignment_visitor clip_distance("gl_ClipDistance"); + + clip_vertex.run(shader->ir); + clip_distance.run(shader->ir); + if (clip_vertex.variable_found() && clip_distance.variable_found()) { + linker_error(prog, "vertex shader writes to both `gl_ClipVertex' " + "and `gl_ClipDistance'\n"); + return false; + } + prog->Vert.UsesClipDistance = clip_distance.variable_found(); + ir_variable *clip_distance_var = + shader->symbols->get_variable("gl_ClipDistance"); + if (clip_distance_var) + prog->Vert.ClipDistanceArraySize = clip_distance_var->type->length; } return true; @@ -393,43 +456,63 @@ cross_validate_globals(struct gl_shader_program *prog, existing->explicit_location = true; } - /* Validate layout qualifiers for gl_FragDepth. - * - * From the AMD/ARB_conservative_depth specs: - * "If gl_FragDepth is redeclared in any fragment shader in - * a program, it must be redeclared in all fragment shaders in that - * program that have static assignments to gl_FragDepth. All - * redeclarations of gl_FragDepth in all fragment shaders in - * a single program must have the same set of qualifiers." - */ - if (strcmp(var->name, "gl_FragDepth") == 0) { - bool layout_declared = var->depth_layout != ir_depth_layout_none; - bool layout_differs = var->depth_layout != existing->depth_layout; - if (layout_declared && layout_differs) { - linker_error(prog, - "All redeclarations of gl_FragDepth in all fragment shaders " - "in a single program must have the same set of qualifiers."); - } - if (var->used && layout_differs) { - linker_error(prog, - "If gl_FragDepth is redeclared with a layout qualifier in" - "any fragment shader, it must be redeclared with the same" - "layout qualifier in all fragment shaders that have" - "assignments to gl_FragDepth"); - } - } - - /* FINISHME: Handle non-constant initializers. + /* Validate layout qualifiers for gl_FragDepth. + * + * From the AMD/ARB_conservative_depth specs: + * + * "If gl_FragDepth is redeclared in any fragment shader in a + * program, it must be redeclared in all fragment shaders in + * that program that have static assignments to + * gl_FragDepth. All redeclarations of gl_FragDepth in all + * fragment shaders in a single program must have the same set + * of qualifiers." + */ + if (strcmp(var->name, "gl_FragDepth") == 0) { + bool layout_declared = var->depth_layout != ir_depth_layout_none; + bool layout_differs = + var->depth_layout != existing->depth_layout; + + if (layout_declared && layout_differs) { + linker_error(prog, + "All redeclarations of gl_FragDepth in all " + "fragment shaders in a single program must have " + "the same set of qualifiers."); + } + + if (var->used && layout_differs) { + linker_error(prog, + "If gl_FragDepth is redeclared with a layout " + "qualifier in any fragment shader, it must be " + "redeclared with the same layout qualifier in " + "all fragment shaders that have assignments to " + "gl_FragDepth"); + } + } + + /* Page 35 (page 41 of the PDF) of the GLSL 4.20 spec says: + * + * "If a shared global has multiple initializers, the + * initializers must all be constant expressions, and they + * must all have the same value. Otherwise, a link error will + * result. (A shared global having only one initializer does + * not require that initializer to be a constant expression.)" + * + * Previous to 4.20 the GLSL spec simply said that initializers + * must have the same value. In this case of non-constant + * initializers, this was impossible to determine. As a result, + * no vendor actually implemented that behavior. The 4.20 + * behavior matches the implemented behavior of at least one other + * vendor, so we'll implement that for all GLSL versions. */ - if (var->constant_value != NULL) { - if (existing->constant_value != NULL) { - if (!var->constant_value->has_value(existing->constant_value)) { + if (var->constant_initializer != NULL) { + if (existing->constant_initializer != NULL) { + if (!var->constant_initializer->has_value(existing->constant_initializer)) { linker_error(prog, "initializers for %s " "`%s' have differing values\n", mode_string(var), var->name); return false; } - } else + } else { /* If the first-seen instance of a particular uniform did not * have an initializer but a later instance does, copy the * initializer to the version stored in the symbol table. @@ -442,8 +525,29 @@ cross_validate_globals(struct gl_shader_program *prog, * FINISHME: modify the shader, and linking with the second * FINISHME: will fail. */ - existing->constant_value = - var->constant_value->clone(ralloc_parent(existing), NULL); + existing->constant_initializer = + var->constant_initializer->clone(ralloc_parent(existing), + NULL); + } + } + + if (var->has_initializer) { + if (existing->has_initializer + && (var->constant_initializer == NULL + || existing->constant_initializer == NULL)) { + linker_error(prog, + "shared global variable `%s' has multiple " + "non-constant initializers.\n", + var->name); + return false; + } + + /* Some instance had an initializer, so keep track of that. In + * this location, all sorts of initializers (constant or + * otherwise) will propagate the existence to the variable + * stored in the symbol table. + */ + existing->has_initializer = true; } if (existing->invariant != var->invariant) { @@ -735,6 +839,7 @@ move_non_declarations(exec_list *instructions, exec_node *last, continue; assert(inst->as_assignment() + || inst->as_call() || ((var != NULL) && (var->mode == ir_var_temporary))); if (make_copies) { @@ -785,6 +890,27 @@ get_main_function_signature(gl_shader *sh) } +/** + * This class is only used in link_intrastage_shaders() below but declaring + * it inside that function leads to compiler warnings with some versions of + * gcc. + */ +class array_sizing_visitor : public ir_hierarchical_visitor { +public: + virtual ir_visitor_status visit(ir_variable *var) + { + if (var->type->is_array() && (var->type->length == 0)) { + const glsl_type *type = + glsl_type::get_array_instance(var->type->fields.array, + var->max_array_access + 1); + assert(type != NULL); + var->type = type; + } + return visit_continue; + } +}; + + /** * Combine a group of shaders for a single stage to generate a linked shader * @@ -935,22 +1061,7 @@ link_intrastage_shaders(void *mem_ctx, * max_array_access field. */ if (linked != NULL) { - class array_sizing_visitor : public ir_hierarchical_visitor { - public: - virtual ir_visitor_status visit(ir_variable *var) - { - if (var->type->is_array() && (var->type->length == 0)) { - const glsl_type *type = - glsl_type::get_array_instance(var->type->fields.array, - var->max_array_access + 1); - - assert(type != NULL); - var->type = type; - } - - return visit_continue; - } - } v; + array_sizing_visitor v; v.run(linked->ir); } @@ -958,13 +1069,6 @@ link_intrastage_shaders(void *mem_ctx, return linked; } - -struct uniform_node { - exec_node link; - struct gl_uniform *u; - unsigned slots; -}; - /** * Update the sizes of linked shader uniform arrays to the maximum * array index used. @@ -1037,151 +1141,6 @@ update_array_sizes(struct gl_shader_program *prog) } } -static void -add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht, - const char *name, const glsl_type *type, GLenum shader_type, - unsigned *next_shader_pos, unsigned *total_uniforms) -{ - if (type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - const glsl_type *field_type = type->fields.structure[i].type; - char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name, - type->fields.structure[i].name); - - add_uniform(mem_ctx, uniforms, ht, field_name, field_type, - shader_type, next_shader_pos, total_uniforms); - } - } else { - uniform_node *n = (uniform_node *) hash_table_find(ht, name); - unsigned int vec4_slots; - const glsl_type *array_elem_type = NULL; - - if (type->is_array()) { - array_elem_type = type->fields.array; - /* Array of structures. */ - if (array_elem_type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - char *elem_name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i); - add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type, - shader_type, next_shader_pos, total_uniforms); - } - return; - } - } - - /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out - * vectors to vec4 slots. - */ - if (type->is_array()) { - if (array_elem_type->is_sampler()) - vec4_slots = type->length; - else - vec4_slots = type->length * array_elem_type->matrix_columns; - } else if (type->is_sampler()) { - vec4_slots = 1; - } else { - vec4_slots = type->matrix_columns; - } - - if (n == NULL) { - n = (uniform_node *) calloc(1, sizeof(struct uniform_node)); - n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform)); - n->slots = vec4_slots; - - n->u->Name = strdup(name); - n->u->Type = type; - n->u->VertPos = -1; - n->u->FragPos = -1; - n->u->GeomPos = -1; - (*total_uniforms)++; - - hash_table_insert(ht, n, name); - uniforms->push_tail(& n->link); - } - - switch (shader_type) { - case GL_VERTEX_SHADER: - n->u->VertPos = *next_shader_pos; - break; - case GL_FRAGMENT_SHADER: - n->u->FragPos = *next_shader_pos; - break; - case GL_GEOMETRY_SHADER: - n->u->GeomPos = *next_shader_pos; - break; - } - - (*next_shader_pos) += vec4_slots; - } -} - -void -assign_uniform_locations(struct gl_shader_program *prog) -{ - /* */ - exec_list uniforms; - unsigned total_uniforms = 0; - hash_table *ht = hash_table_ctor(32, hash_table_string_hash, - hash_table_string_compare); - void *mem_ctx = ralloc_context(NULL); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - unsigned next_position = 0; - - foreach_list(node, prog->_LinkedShaders[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_uniform)) - continue; - - if (strncmp(var->name, "gl_", 3) == 0) { - /* At the moment, we don't allocate uniform locations for - * builtin uniforms. It's permitted by spec, and we'll - * likely switch to doing that at some point, but not yet. - */ - continue; - } - - var->location = next_position; - add_uniform(mem_ctx, &uniforms, ht, var->name, var->type, - prog->_LinkedShaders[i]->Type, - &next_position, &total_uniforms); - } - } - - ralloc_free(mem_ctx); - - gl_uniform_list *ul = (gl_uniform_list *) - calloc(1, sizeof(gl_uniform_list)); - - ul->Size = total_uniforms; - ul->NumUniforms = total_uniforms; - ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform)); - - unsigned idx = 0; - uniform_node *next; - for (uniform_node *node = (uniform_node *) uniforms.head - ; node->link.next != NULL - ; node = next) { - next = (uniform_node *) node->link.next; - - node->link.remove(); - memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform)); - idx++; - - free(node->u); - free(node); - } - - hash_table_dtor(ht); - - prog->Uniforms = ul; -} - - /** * Find a contiguous set of available bits in a bitmask. * @@ -1228,12 +1187,6 @@ find_available_slots(unsigned used_mask, unsigned needed_count) * \return * If locations are successfully assigned, true is returned. Otherwise an * error is emitted to the shader link log and false is returned. - * - * \bug - * Locations set via \c glBindFragDataLocation are not currently supported. - * Only locations assigned automatically by the linker, explicitly set by a - * layout qualifier, or explicitly set by a built-in variable (e.g., \c - * gl_FragColor) are supported for fragment shaders. */ bool assign_attribute_or_color_locations(gl_shader_program *prog, @@ -1257,7 +1210,8 @@ assign_attribute_or_color_locations(gl_shader_program *prog, * 1. Invalidate the location assignments for all vertex shader inputs. * * 2. Assign locations for inputs that have user-defined (via - * glBindVertexAttribLocation) locations. + * glBindVertexAttribLocation) locations and outputs that have + * user-defined locations (via glBindFragDataLocation). * * 3. Sort the attributes without assigned locations by number of slots * required in decreasing order. Fragmentation caused by attribute @@ -1274,72 +1228,7 @@ assign_attribute_or_color_locations(gl_shader_program *prog, (target_index == MESA_SHADER_VERTEX) ? ir_var_in : ir_var_out; - invalidate_variable_locations(sh, direction, generic_base); - - if ((target_index == MESA_SHADER_VERTEX) && (prog->Attributes != NULL)) { - for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) { - ir_variable *const var = - sh->symbols->get_variable(prog->Attributes->Parameters[i].Name); - - /* Note: attributes that occupy multiple slots, such as arrays or - * matrices, may appear in the attrib array multiple times. - */ - if ((var == NULL) || (var->location != -1)) - continue; - - /* From page 61 of the OpenGL 4.0 spec: - * - * "LinkProgram will fail if the attribute bindings assigned by - * BindAttribLocation do not leave not enough space to assign a - * location for an active matrix attribute or an active attribute - * array, both of which require multiple contiguous generic - * attributes." - * - * Previous versions of the spec contain similar language but omit the - * bit about attribute arrays. - * - * Page 61 of the OpenGL 4.0 spec also says: - * - * "It is possible for an application to bind more than one - * attribute name to the same location. This is referred to as - * aliasing. This will only work if only one of the aliased - * attributes is active in the executable program, or if no path - * through the shader consumes more than one attribute of a set - * of attributes aliased to the same location. A link error can - * occur if the linker determines that every path through the - * shader consumes multiple aliased attributes, but - * implementations are not required to generate an error in this - * case." - * - * These two paragraphs are either somewhat contradictory, or I don't - * fully understand one or both of them. - */ - /* FINISHME: The code as currently written does not support attribute - * FINISHME: location aliasing (see comment above). - */ - const int attr = prog->Attributes->Parameters[i].StateIndexes[0]; - const unsigned slots = count_attribute_slots(var->type); - - /* Mask representing the contiguous slots that will be used by this - * attribute. - */ - const unsigned use_mask = (1 << slots) - 1; - - /* Generate a link error if the set of bits requested for this - * attribute overlaps any previously allocated bits. - */ - if ((~(use_mask << attr) & used_locations) != used_locations) { - linker_error(prog, - "insufficient contiguous attribute locations " - "available for vertex shader input `%s'", - var->name); - return false; - } - - var->location = VERT_ATTRIB_GENERIC0 + attr; - used_locations |= (use_mask << attr); - } - } + link_invalidate_variable_locations(sh, direction, generic_base); /* Temporary storage for the set of attributes that need locations assigned. */ @@ -1367,28 +1256,95 @@ assign_attribute_or_color_locations(gl_shader_program *prog, continue; if (var->explicit_location) { - const unsigned slots = count_attribute_slots(var->type); - const unsigned use_mask = (1 << slots) - 1; - const int attr = var->location - generic_base; - if ((var->location >= (int)(max_index + generic_base)) || (var->location < 0)) { linker_error(prog, "invalid explicit location %d specified for `%s'\n", - (var->location < 0) ? var->location : attr, + (var->location < 0) + ? var->location : var->location - generic_base, var->name); return false; - } else if (var->location >= generic_base) { - used_locations |= (use_mask << attr); + } + } else if (target_index == MESA_SHADER_VERTEX) { + unsigned binding; + + if (prog->AttributeBindings->get(binding, var->name)) { + assert(binding >= VERT_ATTRIB_GENERIC0); + var->location = binding; + } + } else if (target_index == MESA_SHADER_FRAGMENT) { + unsigned binding; + + if (prog->FragDataBindings->get(binding, var->name)) { + assert(binding >= FRAG_RESULT_DATA0); + var->location = binding; } } - /* The location was explicitly assigned, nothing to do here. + /* If the variable is not a built-in and has a location statically + * assigned in the shader (presumably via a layout qualifier), make sure + * that it doesn't collide with other assigned locations. Otherwise, + * add it to the list of variables that need linker-assigned locations. */ - if (var->location != -1) + const unsigned slots = count_attribute_slots(var->type); + if (var->location != -1) { + if (var->location >= generic_base) { + /* From page 61 of the OpenGL 4.0 spec: + * + * "LinkProgram will fail if the attribute bindings assigned + * by BindAttribLocation do not leave not enough space to + * assign a location for an active matrix attribute or an + * active attribute array, both of which require multiple + * contiguous generic attributes." + * + * Previous versions of the spec contain similar language but omit + * the bit about attribute arrays. + * + * Page 61 of the OpenGL 4.0 spec also says: + * + * "It is possible for an application to bind more than one + * attribute name to the same location. This is referred to as + * aliasing. This will only work if only one of the aliased + * attributes is active in the executable program, or if no + * path through the shader consumes more than one attribute of + * a set of attributes aliased to the same location. A link + * error can occur if the linker determines that every path + * through the shader consumes multiple aliased attributes, + * but implementations are not required to generate an error + * in this case." + * + * These two paragraphs are either somewhat contradictory, or I + * don't fully understand one or both of them. + */ + /* FINISHME: The code as currently written does not support + * FINISHME: attribute location aliasing (see comment above). + */ + /* Mask representing the contiguous slots that will be used by + * this attribute. + */ + const unsigned attr = var->location - generic_base; + const unsigned use_mask = (1 << slots) - 1; + + /* Generate a link error if the set of bits requested for this + * attribute overlaps any previously allocated bits. + */ + if ((~(use_mask << attr) & used_locations) != used_locations) { + const char *const string = (target_index == MESA_SHADER_VERTEX) + ? "vertex shader input" : "fragment shader output"; + linker_error(prog, + "insufficient contiguous locations " + "available for %s `%s'", string, + var->name); + return false; + } + + used_locations |= (use_mask << attr); + } + continue; + } - to_assign[num_attr].slots = count_attribute_slots(var->type); + to_assign[num_attr].slots = slots; to_assign[num_attr].var = var; num_attr++; } @@ -1426,7 +1382,7 @@ assign_attribute_or_color_locations(gl_shader_program *prog, ? "vertex shader input" : "fragment shader output"; linker_error(prog, - "insufficient contiguous attribute locations " + "insufficient contiguous locations " "available for %s `%s'", string, to_assign[i].var->name); return false; @@ -1463,10 +1419,466 @@ demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode) } +/** + * Data structure tracking information about a transform feedback declaration + * during linking. + */ +class tfeedback_decl +{ +public: + bool init(struct gl_context *ctx, struct gl_shader_program *prog, + const void *mem_ctx, const char *input); + static bool is_same(const tfeedback_decl &x, const tfeedback_decl &y); + bool assign_location(struct gl_context *ctx, struct gl_shader_program *prog, + ir_variable *output_var); + bool accumulate_num_outputs(struct gl_shader_program *prog, unsigned *count); + bool store(struct gl_context *ctx, struct gl_shader_program *prog, + struct gl_transform_feedback_info *info, unsigned buffer, + unsigned varying, const unsigned max_outputs) const; + + + /** + * True if assign_location() has been called for this object. + */ + bool is_assigned() const + { + return this->location != -1; + } + + /** + * Determine whether this object refers to the variable var. + */ + bool matches_var(ir_variable *var) const + { + if (this->is_clip_distance_mesa) + return strcmp(var->name, "gl_ClipDistanceMESA") == 0; + else + return strcmp(var->name, this->var_name) == 0; + } + + /** + * The total number of varying components taken up by this variable. Only + * valid if is_assigned() is true. + */ + unsigned num_components() const + { + if (this->is_clip_distance_mesa) + return this->size; + else + return this->vector_elements * this->matrix_columns * this->size; + } + +private: + /** + * The name that was supplied to glTransformFeedbackVaryings. Used for + * error reporting and glGetTransformFeedbackVarying(). + */ + const char *orig_name; + + /** + * The name of the variable, parsed from orig_name. + */ + const char *var_name; + + /** + * True if the declaration in orig_name represents an array. + */ + bool is_subscripted; + + /** + * If is_subscripted is true, the subscript that was specified in orig_name. + */ + unsigned array_subscript; + + /** + * True if the variable is gl_ClipDistance and the driver lowers + * gl_ClipDistance to gl_ClipDistanceMESA. + */ + bool is_clip_distance_mesa; + + /** + * The vertex shader output location that the linker assigned for this + * variable. -1 if a location hasn't been assigned yet. + */ + int location; + + /** + * If location != -1, the number of vector elements in this variable, or 1 + * if this variable is a scalar. + */ + unsigned vector_elements; + + /** + * If location != -1, the number of matrix columns in this variable, or 1 + * if this variable is not a matrix. + */ + unsigned matrix_columns; + + /** Type of the varying returned by glGetTransformFeedbackVarying() */ + GLenum type; + + /** + * If location != -1, the size that should be returned by + * glGetTransformFeedbackVarying(). + */ + unsigned size; +}; + + +/** + * Initialize this object based on a string that was passed to + * glTransformFeedbackVaryings. If there is a parse error, the error is + * reported using linker_error(), and false is returned. + */ +bool +tfeedback_decl::init(struct gl_context *ctx, struct gl_shader_program *prog, + const void *mem_ctx, const char *input) +{ + /* We don't have to be pedantic about what is a valid GLSL variable name, + * because any variable with an invalid name can't exist in the IR anyway. + */ + + this->location = -1; + this->orig_name = input; + this->is_clip_distance_mesa = false; + + const char *bracket = strrchr(input, '['); + + if (bracket) { + this->var_name = ralloc_strndup(mem_ctx, input, bracket - input); + if (sscanf(bracket, "[%u]", &this->array_subscript) != 1) { + linker_error(prog, "Cannot parse transform feedback varying %s", input); + return false; + } + this->is_subscripted = true; + } else { + this->var_name = ralloc_strdup(mem_ctx, input); + this->is_subscripted = false; + } + + /* For drivers that lower gl_ClipDistance to gl_ClipDistanceMESA, this + * class must behave specially to account for the fact that gl_ClipDistance + * is converted from a float[8] to a vec4[2]. + */ + if (ctx->ShaderCompilerOptions[MESA_SHADER_VERTEX].LowerClipDistance && + strcmp(this->var_name, "gl_ClipDistance") == 0) { + this->is_clip_distance_mesa = true; + } + + return true; +} + + +/** + * Determine whether two tfeedback_decl objects refer to the same variable and + * array index (if applicable). + */ +bool +tfeedback_decl::is_same(const tfeedback_decl &x, const tfeedback_decl &y) +{ + if (strcmp(x.var_name, y.var_name) != 0) + return false; + if (x.is_subscripted != y.is_subscripted) + return false; + if (x.is_subscripted && x.array_subscript != y.array_subscript) + return false; + return true; +} + + +/** + * Assign a location for this tfeedback_decl object based on the location + * assignment in output_var. + * + * If an error occurs, the error is reported through linker_error() and false + * is returned. + */ +bool +tfeedback_decl::assign_location(struct gl_context *ctx, + struct gl_shader_program *prog, + ir_variable *output_var) +{ + if (output_var->type->is_array()) { + /* Array variable */ + const unsigned matrix_cols = + output_var->type->fields.array->matrix_columns; + unsigned actual_array_size = this->is_clip_distance_mesa ? + prog->Vert.ClipDistanceArraySize : output_var->type->array_size(); + + if (this->is_subscripted) { + /* Check array bounds. */ + if (this->array_subscript >= actual_array_size) { + linker_error(prog, "Transform feedback varying %s has index " + "%i, but the array size is %u.", + this->orig_name, this->array_subscript, + actual_array_size); + return false; + } + if (this->is_clip_distance_mesa) { + this->location = + output_var->location + this->array_subscript / 4; + } else { + this->location = + output_var->location + this->array_subscript * matrix_cols; + } + this->size = 1; + } else { + this->location = output_var->location; + this->size = actual_array_size; + } + this->vector_elements = output_var->type->fields.array->vector_elements; + this->matrix_columns = matrix_cols; + if (this->is_clip_distance_mesa) + this->type = GL_FLOAT; + else + this->type = output_var->type->fields.array->gl_type; + } else { + /* Regular variable (scalar, vector, or matrix) */ + if (this->is_subscripted) { + linker_error(prog, "Transform feedback varying %s requested, " + "but %s is not an array.", + this->orig_name, this->var_name); + return false; + } + this->location = output_var->location; + this->size = 1; + this->vector_elements = output_var->type->vector_elements; + this->matrix_columns = output_var->type->matrix_columns; + this->type = output_var->type->gl_type; + } + + /* From GL_EXT_transform_feedback: + * A program will fail to link if: + * + * * the total number of components to capture in any varying + * variable in is greater than the constant + * MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT and the + * buffer mode is SEPARATE_ATTRIBS_EXT; + */ + if (prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS && + this->num_components() > + ctx->Const.MaxTransformFeedbackSeparateComponents) { + linker_error(prog, "Transform feedback varying %s exceeds " + "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS.", + this->orig_name); + return false; + } + + return true; +} + + +bool +tfeedback_decl::accumulate_num_outputs(struct gl_shader_program *prog, + unsigned *count) +{ + if (!this->is_assigned()) { + /* From GL_EXT_transform_feedback: + * A program will fail to link if: + * + * * any variable name specified in the array is not + * declared as an output in the geometry shader (if present) or + * the vertex shader (if no geometry shader is present); + */ + linker_error(prog, "Transform feedback varying %s undeclared.", + this->orig_name); + return false; + } + + unsigned translated_size = this->size; + if (this->is_clip_distance_mesa) + translated_size = (translated_size + 3) / 4; + + *count += translated_size * this->matrix_columns; + + return true; +} + + +/** + * Update gl_transform_feedback_info to reflect this tfeedback_decl. + * + * If an error occurs, the error is reported through linker_error() and false + * is returned. + */ +bool +tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog, + struct gl_transform_feedback_info *info, + unsigned buffer, + unsigned varying, const unsigned max_outputs) const +{ + /* From GL_EXT_transform_feedback: + * A program will fail to link if: + * + * * the total number of components to capture is greater than + * the constant MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT + * and the buffer mode is INTERLEAVED_ATTRIBS_EXT. + */ + if (prog->TransformFeedback.BufferMode == GL_INTERLEAVED_ATTRIBS && + info->BufferStride[buffer] + this->num_components() > + ctx->Const.MaxTransformFeedbackInterleavedComponents) { + linker_error(prog, "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS " + "limit has been exceeded."); + return false; + } + + unsigned translated_size = this->size; + if (this->is_clip_distance_mesa) + translated_size = (translated_size + 3) / 4; + unsigned components_so_far = 0; + for (unsigned index = 0; index < translated_size; ++index) { + for (unsigned v = 0; v < this->matrix_columns; ++v) { + unsigned num_components = this->vector_elements; + assert(info->NumOutputs < max_outputs); + info->Outputs[info->NumOutputs].ComponentOffset = 0; + if (this->is_clip_distance_mesa) { + if (this->is_subscripted) { + num_components = 1; + info->Outputs[info->NumOutputs].ComponentOffset = + this->array_subscript % 4; + } else { + num_components = MIN2(4, this->size - components_so_far); + } + } + info->Outputs[info->NumOutputs].OutputRegister = + this->location + v + index * this->matrix_columns; + info->Outputs[info->NumOutputs].NumComponents = num_components; + info->Outputs[info->NumOutputs].OutputBuffer = buffer; + info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer]; + ++info->NumOutputs; + info->BufferStride[buffer] += num_components; + components_so_far += num_components; + } + } + assert(components_so_far == this->num_components()); + + info->Varyings[varying].Name = ralloc_strdup(prog, this->orig_name); + info->Varyings[varying].Type = this->type; + info->Varyings[varying].Size = this->size; + info->NumVarying++; + + return true; +} + + +/** + * Parse all the transform feedback declarations that were passed to + * glTransformFeedbackVaryings() and store them in tfeedback_decl objects. + * + * If an error occurs, the error is reported through linker_error() and false + * is returned. + */ +static bool +parse_tfeedback_decls(struct gl_context *ctx, struct gl_shader_program *prog, + const void *mem_ctx, unsigned num_names, + char **varying_names, tfeedback_decl *decls) +{ + for (unsigned i = 0; i < num_names; ++i) { + if (!decls[i].init(ctx, prog, mem_ctx, varying_names[i])) + return false; + /* From GL_EXT_transform_feedback: + * A program will fail to link if: + * + * * any two entries in the array specify the same varying + * variable; + * + * We interpret this to mean "any two entries in the array + * specify the same varying variable and array index", since transform + * feedback of arrays would be useless otherwise. + */ + for (unsigned j = 0; j < i; ++j) { + if (tfeedback_decl::is_same(decls[i], decls[j])) { + linker_error(prog, "Transform feedback varying %s specified " + "more than once.", varying_names[i]); + return false; + } + } + } + return true; +} + + +/** + * Assign a location for a variable that is produced in one pipeline stage + * (the "producer") and consumed in the next stage (the "consumer"). + * + * \param input_var is the input variable declaration in the consumer. + * + * \param output_var is the output variable declaration in the producer. + * + * \param input_index is the counter that keeps track of assigned input + * locations in the consumer. + * + * \param output_index is the counter that keeps track of assigned output + * locations in the producer. + * + * It is permissible for \c input_var to be NULL (this happens if a variable + * is output by the producer and consumed by transform feedback, but not + * consumed by the consumer). + * + * If the variable has already been assigned a location, this function has no + * effect. + */ +void +assign_varying_location(ir_variable *input_var, ir_variable *output_var, + unsigned *input_index, unsigned *output_index) +{ + if (output_var->location != -1) { + /* Location already assigned. */ + return; + } + + if (input_var) { + assert(input_var->location == -1); + input_var->location = *input_index; + } + + output_var->location = *output_index; + + /* FINISHME: Support for "varying" records in GLSL 1.50. */ + assert(!output_var->type->is_record()); + + if (output_var->type->is_array()) { + const unsigned slots = output_var->type->length + * output_var->type->fields.array->matrix_columns; + + *output_index += slots; + *input_index += slots; + } else { + const unsigned slots = output_var->type->matrix_columns; + + *output_index += slots; + *input_index += slots; + } +} + + +/** + * Assign locations for all variables that are produced in one pipeline stage + * (the "producer") and consumed in the next stage (the "consumer"). + * + * Variables produced by the producer may also be consumed by transform + * feedback. + * + * \param num_tfeedback_decls is the number of declarations indicating + * variables that may be consumed by transform feedback. + * + * \param tfeedback_decls is a pointer to an array of tfeedback_decl objects + * representing the result of parsing the strings passed to + * glTransformFeedbackVaryings(). assign_location() will be called for + * each of these objects that matches one of the outputs of the + * producer. + * + * When num_tfeedback_decls is nonzero, it is permissible for the consumer to + * be NULL. In this case, varying locations are assigned solely based on the + * requirements of transform feedback. + */ bool assign_varying_locations(struct gl_context *ctx, struct gl_shader_program *prog, - gl_shader *producer, gl_shader *consumer) + gl_shader *producer, gl_shader *consumer, + unsigned num_tfeedback_decls, + tfeedback_decl *tfeedback_decls) { /* FINISHME: Set dynamically when geometry shader support is added. */ unsigned output_index = VERT_RESULT_VAR0; @@ -1483,97 +1895,111 @@ assign_varying_locations(struct gl_context *ctx, * not being inputs. This lets the optimizer eliminate them. */ - invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); - invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0); + link_invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); + if (consumer) + link_invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0); foreach_list(node, producer->ir) { ir_variable *const output_var = ((ir_instruction *) node)->as_variable(); - if ((output_var == NULL) || (output_var->mode != ir_var_out) - || (output_var->location != -1)) - continue; - - ir_variable *const input_var = - consumer->symbols->get_variable(output_var->name); - - if ((input_var == NULL) || (input_var->mode != ir_var_in)) + if ((output_var == NULL) || (output_var->mode != ir_var_out)) continue; - assert(input_var->location == -1); - - output_var->location = output_index; - input_var->location = input_index; - - /* FINISHME: Support for "varying" records in GLSL 1.50. */ - assert(!output_var->type->is_record()); + ir_variable *input_var = + consumer ? consumer->symbols->get_variable(output_var->name) : NULL; - if (output_var->type->is_array()) { - const unsigned slots = output_var->type->length - * output_var->type->fields.array->matrix_columns; + if (input_var && input_var->mode != ir_var_in) + input_var = NULL; - output_index += slots; - input_index += slots; - } else { - const unsigned slots = output_var->type->matrix_columns; + if (input_var) { + assign_varying_location(input_var, output_var, &input_index, + &output_index); + } - output_index += slots; - input_index += slots; + for (unsigned i = 0; i < num_tfeedback_decls; ++i) { + if (!tfeedback_decls[i].is_assigned() && + tfeedback_decls[i].matches_var(output_var)) { + if (output_var->location == -1) { + assign_varying_location(input_var, output_var, &input_index, + &output_index); + } + if (!tfeedback_decls[i].assign_location(ctx, prog, output_var)) + return false; + } } } unsigned varying_vectors = 0; - foreach_list(node, consumer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_in)) - continue; - - if (var->location == -1) { - if (prog->Version <= 120) { - /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec: - * - * Only those varying variables used (i.e. read) in - * the fragment shader executable must be written to - * by the vertex shader executable; declaring - * superfluous varying variables in a vertex shader is - * permissible. - * - * We interpret this text as meaning that the VS must - * write the variable for the FS to read it. See - * "glsl1-varying read but not written" in piglit. - */ - - linker_error(prog, "fragment shader varying %s not written " - "by vertex shader\n.", var->name); - } + if (consumer) { + foreach_list(node, consumer->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_in)) + continue; + + if (var->location == -1) { + if (prog->Version <= 120) { + /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec: + * + * Only those varying variables used (i.e. read) in + * the fragment shader executable must be written to + * by the vertex shader executable; declaring + * superfluous varying variables in a vertex shader is + * permissible. + * + * We interpret this text as meaning that the VS must + * write the variable for the FS to read it. See + * "glsl1-varying read but not written" in piglit. + */ + + linker_error(prog, "fragment shader varying %s not written " + "by vertex shader\n.", var->name); + } - /* An 'in' variable is only really a shader input if its - * value is written by the previous stage. - */ - var->mode = ir_var_auto; - } else { - /* The packing rules are used for vertex shader inputs are also used - * for fragment shader inputs. - */ - varying_vectors += count_attribute_slots(var->type); + /* An 'in' variable is only really a shader input if its + * value is written by the previous stage. + */ + var->mode = ir_var_auto; + } else { + /* The packing rules are used for vertex shader inputs are also + * used for fragment shader inputs. + */ + varying_vectors += count_attribute_slots(var->type); + } } } if (ctx->API == API_OPENGLES2 || prog->Version == 100) { if (varying_vectors > ctx->Const.MaxVarying) { - linker_error(prog, "shader uses too many varying vectors " - "(%u > %u)\n", - varying_vectors, ctx->Const.MaxVarying); - return false; + if (ctx->Const.GLSLSkipStrictMaxVaryingLimitCheck) { + linker_warning(prog, "shader uses too many varying vectors " + "(%u > %u), but the driver will try to optimize " + "them out; this is non-portable out-of-spec " + "behavior\n", + varying_vectors, ctx->Const.MaxVarying); + } else { + linker_error(prog, "shader uses too many varying vectors " + "(%u > %u)\n", + varying_vectors, ctx->Const.MaxVarying); + return false; + } } } else { const unsigned float_components = varying_vectors * 4; if (float_components > ctx->Const.MaxVarying * 4) { - linker_error(prog, "shader uses too many varying components " - "(%u > %u)\n", - float_components, ctx->Const.MaxVarying * 4); - return false; + if (ctx->Const.GLSLSkipStrictMaxVaryingLimitCheck) { + linker_warning(prog, "shader uses too many varying components " + "(%u > %u), but the driver will try to optimize " + "them out; this is non-portable out-of-spec " + "behavior\n", + float_components, ctx->Const.MaxVarying * 4); + } else { + linker_error(prog, "shader uses too many varying components " + "(%u > %u)\n", + float_components, ctx->Const.MaxVarying * 4); + return false; + } } } @@ -1581,9 +2007,162 @@ assign_varying_locations(struct gl_context *ctx, } +/** + * Store transform feedback location assignments into + * prog->LinkedTransformFeedback based on the data stored in tfeedback_decls. + * + * If an error occurs, the error is reported through linker_error() and false + * is returned. + */ +static bool +store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog, + unsigned num_tfeedback_decls, + tfeedback_decl *tfeedback_decls) +{ + bool separate_attribs_mode = + prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS; + + ralloc_free(prog->LinkedTransformFeedback.Varyings); + ralloc_free(prog->LinkedTransformFeedback.Outputs); + + memset(&prog->LinkedTransformFeedback, 0, + sizeof(prog->LinkedTransformFeedback)); + + prog->LinkedTransformFeedback.NumBuffers = + separate_attribs_mode ? num_tfeedback_decls : 1; + + prog->LinkedTransformFeedback.Varyings = + rzalloc_array(prog, + struct gl_transform_feedback_varying_info, + num_tfeedback_decls); + + unsigned num_outputs = 0; + for (unsigned i = 0; i < num_tfeedback_decls; ++i) + if (!tfeedback_decls[i].accumulate_num_outputs(prog, &num_outputs)) + return false; + + prog->LinkedTransformFeedback.Outputs = + rzalloc_array(prog, + struct gl_transform_feedback_output, + num_outputs); + + for (unsigned i = 0; i < num_tfeedback_decls; ++i) { + unsigned buffer = separate_attribs_mode ? i : 0; + if (!tfeedback_decls[i].store(ctx, prog, &prog->LinkedTransformFeedback, + buffer, i, num_outputs)) + return false; + } + assert(prog->LinkedTransformFeedback.NumOutputs == num_outputs); + + return true; +} + +/** + * Store the gl_FragDepth layout in the gl_shader_program struct. + */ +static void +store_fragdepth_layout(struct gl_shader_program *prog) +{ + if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { + return; + } + + struct exec_list *ir = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir; + + /* We don't look up the gl_FragDepth symbol directly because if + * gl_FragDepth is not used in the shader, it's removed from the IR. + * However, the symbol won't be removed from the symbol table. + * + * We're only interested in the cases where the variable is NOT removed + * from the IR. + */ + foreach_list(node, ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if (var == NULL || var->mode != ir_var_out) { + continue; + } + + if (strcmp(var->name, "gl_FragDepth") == 0) { + switch (var->depth_layout) { + case ir_depth_layout_none: + prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_NONE; + return; + case ir_depth_layout_any: + prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_ANY; + return; + case ir_depth_layout_greater: + prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_GREATER; + return; + case ir_depth_layout_less: + prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_LESS; + return; + case ir_depth_layout_unchanged: + prog->FragDepthLayout = FRAG_DEPTH_LAYOUT_UNCHANGED; + return; + default: + assert(0); + return; + } + } + } +} + +/** + * Validate the resources used by a program versus the implementation limits + */ +static bool +check_resources(struct gl_context *ctx, struct gl_shader_program *prog) +{ + static const char *const shader_names[MESA_SHADER_TYPES] = { + "vertex", "fragment", "geometry" + }; + + const unsigned max_samplers[MESA_SHADER_TYPES] = { + ctx->Const.MaxVertexTextureImageUnits, + ctx->Const.MaxTextureImageUnits, + ctx->Const.MaxGeometryTextureImageUnits + }; + + const unsigned max_uniform_components[MESA_SHADER_TYPES] = { + ctx->Const.VertexProgram.MaxUniformComponents, + ctx->Const.FragmentProgram.MaxUniformComponents, + 0 /* FINISHME: Geometry shaders. */ + }; + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + struct gl_shader *sh = prog->_LinkedShaders[i]; + + if (sh == NULL) + continue; + + if (sh->num_samplers > max_samplers[i]) { + linker_error(prog, "Too many %s shader texture samplers", + shader_names[i]); + } + + if (sh->num_uniform_components > max_uniform_components[i]) { + if (ctx->Const.GLSLSkipStrictMaxUniformLimitCheck) { + linker_warning(prog, "Too many %s shader uniform components, " + "but the driver will try to optimize them out; " + "this is non-portable out-of-spec behavior\n", + shader_names[i]); + } else { + linker_error(prog, "Too many %s shader uniform components", + shader_names[i]); + } + } + } + + return prog->LinkStatus; +} + void link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) { + tfeedback_decl *tfeedback_decls = NULL; + unsigned num_tfeedback_decls = prog->TransformFeedback.NumVarying; + void *mem_ctx = ralloc_context(NULL); // temporary linker context prog->LinkStatus = false; @@ -1633,7 +2212,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) * of all shaders must match. */ assert(min_version >= 100); - assert(max_version <= 130); + assert(max_version <= 140); if ((max_version >= 130 || min_version == 100) && min_version != max_version) { linker_error(prog, "all shaders must use same shading " @@ -1724,13 +2303,14 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) if (!prog->LinkStatus) goto done; - while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) - ; - } + if (ctx->ShaderCompilerOptions[i].LowerClipDistance) + lower_clip_distance(prog->_LinkedShaders[i]->ir); - update_array_sizes(prog); + unsigned max_unroll = ctx->ShaderCompilerOptions[i].MaxUnrollIterations; - assign_uniform_locations(prog); + while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, false, max_unroll)) + ; + } /* FINISHME: The value of the max_attribute_index parameter is * FINISHME: implementation dependent based on the value of @@ -1751,22 +2331,63 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) break; } + if (num_tfeedback_decls != 0) { + /* From GL_EXT_transform_feedback: + * A program will fail to link if: + * + * * the specified by TransformFeedbackVaryingsEXT is + * non-zero, but the program object has no vertex or geometry + * shader; + */ + if (prev >= MESA_SHADER_FRAGMENT) { + linker_error(prog, "Transform feedback varyings specified, but " + "no vertex or geometry shader is present."); + goto done; + } + + tfeedback_decls = ralloc_array(mem_ctx, tfeedback_decl, + prog->TransformFeedback.NumVarying); + if (!parse_tfeedback_decls(ctx, prog, mem_ctx, num_tfeedback_decls, + prog->TransformFeedback.VaryingNames, + tfeedback_decls)) + goto done; + } + for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { if (prog->_LinkedShaders[i] == NULL) continue; - if (!assign_varying_locations(ctx, prog, - prog->_LinkedShaders[prev], - prog->_LinkedShaders[i])) { + if (!assign_varying_locations( + ctx, prog, prog->_LinkedShaders[prev], prog->_LinkedShaders[i], + i == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0, + tfeedback_decls)) goto done; - } prev = i; } + if (prev != MESA_SHADER_FRAGMENT && num_tfeedback_decls != 0) { + /* There was no fragment shader, but we still have to assign varying + * locations for use by transform feedback. + */ + if (!assign_varying_locations( + ctx, prog, prog->_LinkedShaders[prev], NULL, num_tfeedback_decls, + tfeedback_decls)) + goto done; + } + + if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls)) + goto done; + if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX], ir_var_out); + + /* Eliminate code that is now dead due to unused vertex outputs being + * demoted. + */ + while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_VERTEX]->ir, false)) + ; } if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { @@ -1775,19 +2396,40 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) demote_shader_inputs_and_outputs(sh, ir_var_in); demote_shader_inputs_and_outputs(sh, ir_var_inout); demote_shader_inputs_and_outputs(sh, ir_var_out); + + /* Eliminate code that is now dead due to unused geometry outputs being + * demoted. + */ + while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_GEOMETRY]->ir, false)) + ; } if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) { gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]; demote_shader_inputs_and_outputs(sh, ir_var_in); + + /* Eliminate code that is now dead due to unused fragment inputs being + * demoted. This shouldn't actually do anything other than remove + * declarations of the (now unused) global variables. + */ + while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir, false)) + ; } + update_array_sizes(prog); + link_assign_uniform_locations(prog); + store_fragdepth_layout(prog); + + if (!check_resources(ctx, prog)) + goto done; + /* OpenGL ES requires that a vertex shader and a fragment shader both be * present in a linked program. By checking for use of shading language * version 1.00, we also catch the GL_ARB_ES2_compatibility case. */ - if (ctx->API == API_OPENGLES2 || prog->Version == 100) { + if (!prog->InternalSeparateShader && + (ctx->API == API_OPENGLES2 || prog->Version == 100)) { if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) { linker_error(prog, "program lacks a vertex shader\n"); } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { @@ -1806,6 +2448,14 @@ done: /* Retain any live IR, but trash the rest. */ reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir); + + /* The symbol table in the linked shaders may contain references to + * variables that were removed (e.g., unused uniforms). Since it may + * contain junk, there is no possible valid use. Delete it and set the + * pointer to NULL. + */ + delete prog->_LinkedShaders[i]->symbols; + prog->_LinkedShaders[i]->symbols = NULL; } ralloc_free(mem_ctx);