X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flinker.cpp;h=82bddb003c3fb0077f63da91b6e2b8a249210a23;hb=ead3589aa2810b66164178a1d55d2063cfa3b041;hp=255edc6a76f179d2198805c8db0e0b8c780a7958;hpb=f18a086326b2bc832a3c181e6bbec6a62e1b3fcd;p=mesa.git diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index 255edc6a76f..82bddb003c3 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -164,7 +164,7 @@ private: void -linker_error_printf(gl_shader_program *prog, const char *fmt, ...) +linker_error(gl_shader_program *prog, const char *fmt, ...) { va_list ap; @@ -172,12 +172,27 @@ linker_error_printf(gl_shader_program *prog, const char *fmt, ...) va_start(ap, fmt); ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); va_end(ap); + + prog->LinkStatus = false; +} + + +void +linker_warning(gl_shader_program *prog, const char *fmt, ...) +{ + va_list ap; + + ralloc_strcat(&prog->InfoLog, "error: "); + va_start(ap, fmt); + ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); + va_end(ap); + } 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(); @@ -229,7 +244,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 */ @@ -243,11 +261,36 @@ validate_vertex_shader_executable(struct gl_shader_program *prog, find_assignment_visitor find("gl_Position"); find.run(shader->ir); if (!find.variable_found()) { - linker_error_printf(prog, - "vertex shader does not write to `gl_Position'\n"); + 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; } @@ -271,8 +314,8 @@ validate_fragment_shader_executable(struct gl_shader_program *prog, frag_data.run(shader->ir); if (frag_color.variable_found() && frag_data.variable_found()) { - linker_error_printf(prog, "fragment shader writes to both " - "`gl_FragColor' and `gl_FragData'\n"); + linker_error(prog, "fragment shader writes to both " + "`gl_FragColor' and `gl_FragData'\n"); return false; } @@ -357,11 +400,11 @@ cross_validate_globals(struct gl_shader_program *prog, existing->type = var->type; } } else { - linker_error_printf(prog, "%s `%s' declared as type " - "`%s' and type `%s'\n", - mode_string(var), - var->name, var->type->name, - existing->type->name); + linker_error(prog, "%s `%s' declared as type " + "`%s' and type `%s'\n", + mode_string(var), + var->name, var->type->name, + existing->type->name); return false; } } @@ -369,9 +412,9 @@ cross_validate_globals(struct gl_shader_program *prog, if (var->explicit_location) { if (existing->explicit_location && (var->location != existing->location)) { - linker_error_printf(prog, "explicit locations for %s " - "`%s' have differing values\n", - mode_string(var), var->name); + linker_error(prog, "explicit locations for %s " + "`%s' have differing values\n", + mode_string(var), var->name); return false; } @@ -379,43 +422,63 @@ cross_validate_globals(struct gl_shader_program *prog, existing->explicit_location = true; } - /* Validate layout qualifiers for gl_FragDepth. - * - * From the AMD_conservative_depth spec: - * "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_printf(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_printf(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 (var->constant_value != NULL) { - if (existing->constant_value != NULL) { - if (!var->constant_value->has_value(existing->constant_value)) { - linker_error_printf(prog, "initializers for %s " - "`%s' have differing values\n", - mode_string(var), var->name); + 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_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. @@ -428,20 +491,41 @@ 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) { - linker_error_printf(prog, "declarations for %s `%s' have " - "mismatching invariant qualifiers\n", - mode_string(var), var->name); + linker_error(prog, "declarations for %s `%s' have " + "mismatching invariant qualifiers\n", + mode_string(var), var->name); return false; } if (existing->centroid != var->centroid) { - linker_error_printf(prog, "declarations for %s `%s' have " - "mismatching centroid qualifiers\n", - mode_string(var), var->name); + linker_error(prog, "declarations for %s `%s' have " + "mismatching centroid qualifiers\n", + mode_string(var), var->name); return false; } } else @@ -529,13 +613,12 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog, */ if (!output->type->is_array() || (strncmp("gl_", output->name, 3) != 0)) { - linker_error_printf(prog, - "%s shader output `%s' declared as " - "type `%s', but %s shader input declared " - "as type `%s'\n", - producer_stage, output->name, - output->type->name, - consumer_stage, input->type->name); + linker_error(prog, + "%s shader output `%s' declared as type `%s', " + "but %s shader input declared as type `%s'\n", + producer_stage, output->name, + output->type->name, + consumer_stage, input->type->name); return false; } } @@ -543,40 +626,40 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog, /* Check that all of the qualifiers match between stages. */ if (input->centroid != output->centroid) { - linker_error_printf(prog, - "%s shader output `%s' %s centroid qualifier, " - "but %s shader input %s centroid qualifier\n", - producer_stage, - output->name, - (output->centroid) ? "has" : "lacks", - consumer_stage, - (input->centroid) ? "has" : "lacks"); + linker_error(prog, + "%s shader output `%s' %s centroid qualifier, " + "but %s shader input %s centroid qualifier\n", + producer_stage, + output->name, + (output->centroid) ? "has" : "lacks", + consumer_stage, + (input->centroid) ? "has" : "lacks"); return false; } if (input->invariant != output->invariant) { - linker_error_printf(prog, - "%s shader output `%s' %s invariant qualifier, " - "but %s shader input %s invariant qualifier\n", - producer_stage, - output->name, - (output->invariant) ? "has" : "lacks", - consumer_stage, - (input->invariant) ? "has" : "lacks"); + linker_error(prog, + "%s shader output `%s' %s invariant qualifier, " + "but %s shader input %s invariant qualifier\n", + producer_stage, + output->name, + (output->invariant) ? "has" : "lacks", + consumer_stage, + (input->invariant) ? "has" : "lacks"); return false; } if (input->interpolation != output->interpolation) { - linker_error_printf(prog, - "%s shader output `%s' specifies %s " - "interpolation qualifier, " - "but %s shader input specifies %s " - "interpolation qualifier\n", - producer_stage, - output->name, - output->interpolation_string(), - consumer_stage, - input->interpolation_string()); + linker_error(prog, + "%s shader output `%s' specifies %s " + "interpolation qualifier, " + "but %s shader input specifies %s " + "interpolation qualifier\n", + producer_stage, + output->name, + output->interpolation_string(), + consumer_stage, + input->interpolation_string()); return false; } } @@ -772,6 +855,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 * @@ -823,9 +927,8 @@ link_intrastage_shaders(void *mem_ctx, if ((other_sig != NULL) && other_sig->is_defined && !other_sig->is_builtin) { - linker_error_printf(prog, - "function `%s' is multiply defined", - f->name); + linker_error(prog, "function `%s' is multiply defined", + f->name); return NULL; } } @@ -849,9 +952,9 @@ link_intrastage_shaders(void *mem_ctx, } if (main == NULL) { - linker_error_printf(prog, "%s shader lacks `main'\n", - (shader_list[0]->Type == GL_VERTEX_SHADER) - ? "vertex" : "fragment"); + linker_error(prog, "%s shader lacks `main'\n", + (shader_list[0]->Type == GL_VERTEX_SHADER) + ? "vertex" : "fragment"); return NULL; } @@ -910,27 +1013,20 @@ link_intrastage_shaders(void *mem_ctx, free(linking_shaders); +#ifdef DEBUG + /* At this point linked should contain all of the linked IR, so + * validate it to make sure nothing went wrong. + */ + if (linked) + validate_ir_tree(linked->ir); +#endif + /* Make a pass over all variable declarations to ensure that arrays with * unspecified sizes have a size specified. The size is inferred from the * 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); } @@ -938,13 +1034,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. @@ -1017,151 +1106,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. * @@ -1194,23 +1138,45 @@ find_available_slots(unsigned used_mask, unsigned needed_count) } +/** + * Assign locations for either VS inputs for FS outputs + * + * \param prog Shader program whose variables need locations assigned + * \param target_index Selector for the program target to receive location + * assignmnets. Must be either \c MESA_SHADER_VERTEX or + * \c MESA_SHADER_FRAGMENT. + * \param max_index Maximum number of generic locations. This corresponds + * to either the maximum number of draw buffers or the + * maximum number of generic attributes. + * + * \return + * If locations are successfully assigned, true is returned. Otherwise an + * error is emitted to the shader link log and false is returned. + */ bool -assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index) +assign_attribute_or_color_locations(gl_shader_program *prog, + unsigned target_index, + unsigned max_index) { - /* Mark invalid attribute locations as being used. + /* Mark invalid locations as being used. */ - unsigned used_locations = (max_attribute_index >= 32) - ? ~0 : ~((1 << max_attribute_index) - 1); + unsigned used_locations = (max_index >= 32) + ? ~0 : ~((1 << max_index) - 1); + + assert((target_index == MESA_SHADER_VERTEX) + || (target_index == MESA_SHADER_FRAGMENT)); - gl_shader *const sh = prog->_LinkedShaders[0]; - assert(sh->Type == GL_VERTEX_SHADER); + gl_shader *const sh = prog->_LinkedShaders[target_index]; + if (sh == NULL) + return true; /* Operate in a total of four passes. * * 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 @@ -1220,72 +1186,14 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index * 4. Assign locations to any inputs without assigned locations. */ - invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0); - - if (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); + const int generic_base = (target_index == MESA_SHADER_VERTEX) + ? (int) VERT_ATTRIB_GENERIC0 : (int) FRAG_RESULT_DATA0; - /* Mask representing the contiguous slots that will be used by this - * attribute. - */ - const unsigned use_mask = (1 << slots) - 1; + const enum ir_variable_mode direction = + (target_index == MESA_SHADER_VERTEX) ? ir_var_in : ir_var_out; - /* 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_printf(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. */ @@ -1309,33 +1217,97 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index foreach_list(node, sh->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); - if ((var == NULL) || (var->mode != ir_var_in)) + if ((var == NULL) || (var->mode != (unsigned) direction)) 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 - VERT_ATTRIB_GENERIC0; - - if ((var->location >= (int)(max_attribute_index + VERT_ATTRIB_GENERIC0)) + if ((var->location >= (int)(max_index + generic_base)) || (var->location < 0)) { - linker_error_printf(prog, - "invalid explicit location %d specified for " - "`%s'\n", - (var->location < 0) ? var->location : attr, - var->name); + linker_error(prog, + "invalid explicit location %d specified for `%s'\n", + (var->location < 0) + ? var->location : var->location - generic_base, + var->name); return false; - } else if (var->location >= VERT_ATTRIB_GENERIC0) { - 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) { + linker_error(prog, + "insufficient contiguous attribute locations " + "available for vertex shader input `%s'", + 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++; } @@ -1349,14 +1321,16 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare); - /* VERT_ATTRIB_GENERIC0 is a pseudo-alias for VERT_ATTRIB_POS. It can only - * be explicitly assigned by via glBindAttribLocation. Mark it as reserved - * to prevent it from being automatically allocated below. - */ - find_deref_visitor find("gl_Vertex"); - find.run(sh->ir); - if (find.variable_found()) - used_locations |= (1 << 0); + if (target_index == MESA_SHADER_VERTEX) { + /* VERT_ATTRIB_GENERIC0 is a pseudo-alias for VERT_ATTRIB_POS. It can + * only be explicitly assigned by via glBindAttribLocation. Mark it as + * reserved to prevent it from being automatically allocated below. + */ + find_deref_visitor find("gl_Vertex"); + find.run(sh->ir); + if (find.variable_found()) + used_locations |= (1 << 0); + } for (unsigned i = 0; i < num_attr; i++) { /* Mask representing the contiguous slots that will be used by this @@ -1367,14 +1341,17 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index int location = find_available_slots(used_locations, to_assign[i].slots); if (location < 0) { - linker_error_printf(prog, - "insufficient contiguous attribute locations " - "available for vertex shader input `%s'", - to_assign[i].var->name); + const char *const string = (target_index == MESA_SHADER_VERTEX) + ? "vertex shader input" : "fragment shader output"; + + linker_error(prog, + "insufficient contiguous attribute locations " + "available for %s `%s'", + string, to_assign[i].var->name); return false; } - to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location; + to_assign[i].var->location = generic_base + location; used_locations |= (use_mask << location); } @@ -1405,9 +1382,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_locations(struct gl_shader_program *prog, - gl_shader *producer, gl_shader *consumer) +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, + unsigned num_tfeedback_decls, + tfeedback_decl *tfeedback_decls) { /* FINISHME: Set dynamically when geometry shader support is added. */ unsigned output_index = VERT_RESULT_VAR0; @@ -1424,82 +1858,274 @@ assign_varying_locations(struct gl_shader_program *prog, * 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)) + if ((output_var == NULL) || (output_var->mode != ir_var_out)) continue; - ir_variable *const input_var = - consumer->symbols->get_variable(output_var->name); + ir_variable *input_var = + consumer ? consumer->symbols->get_variable(output_var->name) : NULL; - if ((input_var == NULL) || (input_var->mode != ir_var_in)) - continue; + if (input_var && input_var->mode != ir_var_in) + input_var = NULL; - assert(input_var->location == -1); - - output_var->location = output_index; - input_var->location = input_index; + if (input_var) { + assign_varying_location(input_var, output_var, &input_index, + &output_index); + } - /* FINISHME: Support for "varying" records in GLSL 1.50. */ - assert(!output_var->type->is_record()); + 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; + } + } + } - if (output_var->type->is_array()) { - const unsigned slots = output_var->type->length - * output_var->type->fields.array->matrix_columns; + unsigned varying_vectors = 0; + + 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); + } - output_index += slots; - input_index += slots; - } else { - const unsigned slots = output_var->type->matrix_columns; + /* 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); + } + } + } - output_index += slots; - input_index += slots; + if (ctx->API == API_OPENGLES2 || prog->Version == 100) { + if (varying_vectors > ctx->Const.MaxVarying) { + 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) { + 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; + } } } - foreach_list(node, consumer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); + return true; +} - 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. - */ +/** + * 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); - linker_error_printf(prog, "fragment shader varying %s not written " - "by vertex shader\n.", var->name); - prog->LinkStatus = false; - } + return true; +} - /* An 'in' variable is only really a shader input if its - * value is written by the previous stage. - */ - var->mode = ir_var_auto; +/** + * 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; @@ -1552,8 +2178,8 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) assert(max_version <= 130); if ((max_version >= 130 || min_version == 100) && min_version != max_version) { - linker_error_printf(prog, "all shaders must use same shading " - "language version\n"); + linker_error(prog, "all shaders must use same shading " + "language version\n"); goto done; } @@ -1636,24 +2262,28 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) if (prog->_LinkedShaders[i] == NULL) continue; - while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) + detect_recursion_linked(prog, prog->_LinkedShaders[i]->ir); + if (!prog->LinkStatus) + goto done; + + if (ctx->ShaderCompilerOptions[i].LowerClipDistance) + lower_clip_distance(prog->_LinkedShaders[i]->ir); + + while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, false, 32)) ; } - update_array_sizes(prog); - - assign_uniform_locations(prog); + /* FINISHME: The value of the max_attribute_index parameter is + * FINISHME: implementation dependent based on the value of + * FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be + * FINISHME: at least 16, so hardcode 16 for now. + */ + if (!assign_attribute_or_color_locations(prog, MESA_SHADER_VERTEX, 16)) { + goto done; + } - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { - /* FINISHME: The value of the max_attribute_index parameter is - * FINISHME: implementation dependent based on the value of - * FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be - * FINISHME: at least 16, so hardcode 16 for now. - */ - if (!assign_attribute_locations(prog, 16)) { - prog->LinkStatus = false; - goto done; - } + if (!assign_attribute_or_color_locations(prog, MESA_SHADER_FRAGMENT, ctx->Const.MaxDrawBuffers)) { + goto done; } unsigned prev; @@ -1662,19 +2292,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; - assign_varying_locations(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) { @@ -1683,25 +2357,44 @@ 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_printf(prog, "program lacks a vertex shader\n"); - prog->LinkStatus = false; + linker_error(prog, "program lacks a vertex shader\n"); } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { - linker_error_printf(prog, "program lacks a fragment shader\n"); - prog->LinkStatus = false; + linker_error(prog, "program lacks a fragment shader\n"); } } @@ -1716,6 +2409,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);