X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flinker.cpp;h=8d14c5afdff514c79f8bdff5a63a0cf508ca6bae;hb=001eee52d461233b1e1d6ed3577965e9bcb209e8;hp=94db57d6a52994e7d64e30192a4f04e5e0e9891c;hpb=046bef235744e891e4a48076e1a3ff9a61a63092;p=mesa.git diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index 94db57d6a52..8d14c5afdff 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -72,9 +72,7 @@ extern "C" { #include } -#include "main/mtypes.h" -#include "main/macros.h" -#include "main/shaderobj.h" +#include "main/core.h" #include "glsl_symbol_table.h" #include "ir.h" #include "program.h" @@ -82,6 +80,10 @@ extern "C" { #include "linker.h" #include "ir_optimization.h" +extern "C" { +#include "main/shaderobj.h" +} + /** * Visitor that determines whether or not a variable is ever written. */ @@ -105,6 +107,27 @@ public: return visit_continue_with_parent; } + virtual ir_visitor_status visit_enter(ir_call *ir) + { + exec_list_iterator sig_iter = ir->get_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(); + + if (sig_param->mode == ir_var_out || + sig_param->mode == ir_var_inout) { + ir_variable *var = param_rval->variable_referenced(); + if (var && strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } + } + sig_iter.next(); + } + + return visit_continue_with_parent; + } + bool variable_found() { return found; @@ -116,6 +139,38 @@ private: }; +/** + * Visitor that determines whether or not a variable is ever read. + */ +class find_deref_visitor : public ir_hierarchical_visitor { +public: + find_deref_visitor(const char *name) + : name(name), found(false) + { + /* empty */ + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + if (strcmp(this->name, ir->var->name) == 0) { + this->found = true; + return visit_stop; + } + + return visit_continue; + } + + bool variable_found() const + { + return this->found; + } + +private: + const char *name; /**< Find writes to a variable with this name. */ + bool found; /**< Was a write to the variable found? */ +}; + + void linker_error_printf(gl_shader_program *prog, const char *fmt, ...) { @@ -140,7 +195,7 @@ invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, /* Only assign locations for generic attributes / varyings / etc. */ - if (var->location >= generic_base) + if ((var->location >= generic_base) && !var->explicit_location) var->location = -1; } } @@ -270,6 +325,9 @@ cross_validate_globals(struct gl_shader_program *prog, */ glsl_symbol_table variables; for (unsigned i = 0; i < num_shaders; i++) { + if (shader_list[i] == NULL) + continue; + foreach_list(node, shader_list[i]->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); @@ -292,12 +350,39 @@ cross_validate_globals(struct gl_shader_program *prog, ir_variable *const existing = variables.get_variable(var->name); if (existing != NULL) { if (var->type != existing->type) { - 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); - return false; + /* Consider the types to be "the same" if both types are arrays + * of the same type and one of the arrays is implicitly sized. + * In addition, set the type of the linked variable to the + * explicitly sized array. + */ + if (var->type->is_array() + && existing->type->is_array() + && (var->type->fields.array == existing->type->fields.array) + && ((var->type->length == 0) + || (existing->type->length == 0))) { + if (existing->type->length == 0) + 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); + return false; + } + } + + 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); + return false; + } + + existing->location = var->location; + existing->explicit_location = true; } /* FINISHME: Handle non-constant initializers. @@ -327,7 +412,7 @@ cross_validate_globals(struct gl_shader_program *prog, var->constant_value->clone(talloc_parent(existing), NULL); } } else - variables.add_variable(var->name, var); + variables.add_variable(var); } } @@ -342,7 +427,7 @@ bool cross_validate_uniforms(struct gl_shader_program *prog) { return cross_validate_globals(prog, prog->_LinkedShaders, - prog->_NumLinkedShaders, true); + MESA_SHADER_TYPES, true); } @@ -369,7 +454,7 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog, if ((var == NULL) || (var->mode != ir_var_out)) continue; - parameters.add_variable(var->name, var); + parameters.add_variable(var); } @@ -392,7 +477,7 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog, */ if (input->type != output->type) { linker_error_printf(prog, - "%s shader output `%s' delcared as " + "%s shader output `%s' declared as " "type `%s', but %s shader input declared " "as type `%s'\n", producer_stage, output->name, @@ -461,9 +546,9 @@ populate_symbol_table(gl_shader *sh) ir_function *func; if ((func = inst->as_function()) != NULL) { - sh->symbols->add_function(func->name, func); + sh->symbols->add_function(func); } else if ((var = inst->as_variable()) != NULL) { - sh->symbols->add_variable(var->name, var); + sh->symbols->add_variable(var); } } } @@ -520,7 +605,7 @@ remap_variables(ir_instruction *inst, struct gl_shader *target, else { ir_variable *copy = ir->var->clone(this->target, NULL); - this->symbols->add_variable(copy->name, copy); + this->symbols->add_variable(copy); this->instructions->push_head(copy); ir->var = copy; } @@ -641,7 +726,8 @@ get_main_function_signature(gl_shader *sh) * shader is returned. */ static struct gl_shader * -link_intrastage_shaders(struct gl_shader_program *prog, +link_intrastage_shaders(struct gl_context *ctx, + struct gl_shader_program *prog, struct gl_shader **shader_list, unsigned num_shaders) { @@ -674,14 +760,14 @@ link_intrastage_shaders(struct gl_shader_program *prog, ir_function_signature *sig = (ir_function_signature *) iter.get(); - if (!sig->is_defined || sig->is_built_in) + if (!sig->is_defined || sig->is_builtin) continue; ir_function_signature *other_sig = other->exact_matching_signature(& sig->parameters); if ((other_sig != NULL) && other_sig->is_defined - && !other_sig->is_built_in) { + && !other_sig->is_builtin) { linker_error_printf(prog, "function `%s' is multiply defined", f->name); @@ -714,7 +800,7 @@ link_intrastage_shaders(struct gl_shader_program *prog, return NULL; } - gl_shader *const linked = _mesa_new_shader(NULL, 0, main->Type); + gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type); linked->ir = new(linked) exec_list; clone_ir_list(linked, linked->ir, main->ir); @@ -761,7 +847,11 @@ link_intrastage_shaders(struct gl_shader_program *prog, assert(idx == num_linking_shaders); - link_function_calls(prog, linked, linking_shaders, num_linking_shaders); + if (!link_function_calls(prog, linked, linking_shaders, + num_linking_shaders)) { + ctx->Driver.DeleteShader(ctx, linked); + linked = NULL; + } free(linking_shaders); @@ -775,6 +865,143 @@ struct uniform_node { unsigned slots; }; +/** + * Update the sizes of linked shader uniform arrays to the maximum + * array index used. + * + * From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec: + * + * If one or more elements of an array are active, + * GetActiveUniform will return the name of the array in name, + * subject to the restrictions listed above. The type of the array + * is returned in type. The size parameter contains the highest + * array element index used, plus one. The compiler or linker + * determines the highest index used. There will be only one + * active uniform reported by the GL per uniform array. + + */ +static void +update_array_sizes(struct gl_shader_program *prog) +{ + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + foreach_list(node, prog->_LinkedShaders[i]->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_uniform && + var->mode != ir_var_in && + var->mode != ir_var_out) || + !var->type->is_array()) + continue; + + unsigned int size = var->max_array_access; + for (unsigned j = 0; j < MESA_SHADER_TYPES; j++) { + if (prog->_LinkedShaders[j] == NULL) + continue; + + foreach_list(node2, prog->_LinkedShaders[j]->ir) { + ir_variable *other_var = ((ir_instruction *) node2)->as_variable(); + if (!other_var) + continue; + + if (strcmp(var->name, other_var->name) == 0 && + other_var->max_array_access > size) { + size = other_var->max_array_access; + } + } + } + + if (size + 1 != var->type->fields.array->length) { + var->type = glsl_type::get_array_instance(var->type->fields.array, + size + 1); + /* FINISHME: We should update the types of array + * dereferences of this variable now. + */ + } + } + } +} + +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 = talloc_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 = talloc_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) { @@ -783,8 +1010,12 @@ assign_uniform_locations(struct gl_shader_program *prog) unsigned total_uniforms = 0; hash_table *ht = hash_table_ctor(32, hash_table_string_hash, hash_table_string_compare); + void *mem_ctx = talloc_new(NULL); + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; - for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { unsigned next_position = 0; foreach_list(node, prog->_LinkedShaders[i]->ir) { @@ -793,52 +1024,23 @@ assign_uniform_locations(struct gl_shader_program *prog) if ((var == NULL) || (var->mode != ir_var_uniform)) continue; - if (var->type->is_sampler()) + 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; - - const unsigned vec4_slots = (var->component_slots() + 3) / 4; - assert(vec4_slots != 0); - - uniform_node *n = (uniform_node *) hash_table_find(ht, var->name); - if (n == NULL) { - n = (uniform_node *) calloc(1, sizeof(struct uniform_node)); - n->u = (gl_uniform *) calloc(vec4_slots, sizeof(struct gl_uniform)); - n->slots = vec4_slots; - - n->u[0].Name = strdup(var->name); - for (unsigned j = 1; j < vec4_slots; j++) - n->u[j].Name = n->u[0].Name; - - hash_table_insert(ht, n, n->u[0].Name); - uniforms.push_tail(& n->link); - total_uniforms += vec4_slots; } - if (var->constant_value != NULL) - for (unsigned j = 0; j < vec4_slots; j++) - n->u[j].Initialized = true; - var->location = next_position; - - for (unsigned j = 0; j < vec4_slots; j++) { - switch (prog->_LinkedShaders[i]->Type) { - case GL_VERTEX_SHADER: - n->u[j].VertPos = next_position; - break; - case GL_FRAGMENT_SHADER: - n->u[j].FragPos = next_position; - break; - case GL_GEOMETRY_SHADER: - /* FINISHME: Support geometry shaders. */ - assert(prog->_LinkedShaders[i]->Type != GL_GEOMETRY_SHADER); - break; - } - - next_position++; - } + add_uniform(mem_ctx, &uniforms, ht, var->name, var->type, + prog->_LinkedShaders[i]->Type, + &next_position, &total_uniforms); } } + talloc_free(mem_ctx); + gl_uniform_list *ul = (gl_uniform_list *) calloc(1, sizeof(gl_uniform_list)); @@ -854,8 +1056,8 @@ assign_uniform_locations(struct gl_shader_program *prog) next = (uniform_node *) node->link.next; node->link.remove(); - memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform) * node->slots); - idx += node->slots; + memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform)); + idx++; free(node->u); free(node); @@ -1017,6 +1219,24 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index if ((var == NULL) || (var->mode != ir_var_in)) 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)) + || (var->location < 0)) { + linker_error_printf(prog, + "invalid explicit location %d specified for " + "`%s'\n", + (var->location < 0) ? var->location : attr, + var->name); + return false; + } else if (var->location >= VERT_ATTRIB_GENERIC0) { + used_locations |= (use_mask << attr); + } + } + /* The location was explicitly assigned, nothing to do here. */ if (var->location != -1) @@ -1040,7 +1260,10 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index * be explicitly assigned by via glBindAttribLocation. Mark it as reserved * to prevent it from being automatically allocated below. */ - used_locations |= (1 << 0); + 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 @@ -1066,6 +1289,29 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index } +/** + * Demote shader inputs and outputs that are not used in other stages + */ +void +demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode) +{ + foreach_list(node, sh->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != int(mode))) + continue; + + /* A shader 'in' or 'out' variable is only really an input or output if + * its value is used by other shader stages. This will cause the variable + * to have a location assigned. + */ + if (var->location == -1) { + var->mode = ir_var_auto; + } + } +} + + void assign_varying_locations(struct gl_shader_program *prog, gl_shader *producer, gl_shader *consumer) @@ -1103,28 +1349,23 @@ assign_varying_locations(struct gl_shader_program *prog, assert(input_var->location == -1); - /* FINISHME: Location assignment will need some changes when arrays, - * FINISHME: matrices, and structures are allowed as shader inputs / - * FINISHME: outputs. - */ output_var->location = output_index; input_var->location = input_index; - output_index++; - input_index++; - } + /* FINISHME: Support for "varying" records in GLSL 1.50. */ + assert(!output_var->type->is_record()); - foreach_list(node, producer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); + if (output_var->type->is_array()) { + const unsigned slots = output_var->type->length + * output_var->type->fields.array->matrix_columns; - if ((var == NULL) || (var->mode != ir_var_out)) - continue; + output_index += slots; + input_index += slots; + } else { + const unsigned slots = output_var->type->matrix_columns; - /* An 'out' variable is only really a shader output if its value is read - * by the following stage. - */ - if (var->location == -1) { - var->mode = ir_var_auto; + output_index += slots; + input_index += slots; } } @@ -1164,7 +1405,7 @@ assign_varying_locations(struct gl_shader_program *prog, void -link_shaders(struct gl_shader_program *prog) +link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) { prog->LinkStatus = false; prog->Validated = false; @@ -1212,9 +1453,10 @@ link_shaders(struct gl_shader_program *prog) * match shading language versions. With GLSL 1.30 and later, the versions * of all shaders must match. */ - assert(min_version >= 110); + assert(min_version >= 100); assert(max_version <= 130); - if ((max_version >= 130) && (min_version != max_version)) { + if ((max_version >= 130 || min_version == 100) + && min_version != max_version) { linker_error_printf(prog, "all shaders must use same shading " "language version\n"); goto done; @@ -1222,35 +1464,41 @@ link_shaders(struct gl_shader_program *prog) prog->Version = max_version; + for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] != NULL) + ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]); + + prog->_LinkedShaders[i] = NULL; + } + /* Link all shaders for a particular stage and validate the result. */ - prog->_NumLinkedShaders = 0; if (num_vert_shaders > 0) { gl_shader *const sh = - link_intrastage_shaders(prog, vert_shader_list, num_vert_shaders); + link_intrastage_shaders(ctx, prog, vert_shader_list, num_vert_shaders); if (sh == NULL) goto done; if (!validate_vertex_shader_executable(prog, sh)) - goto done; + goto done; - prog->_LinkedShaders[prog->_NumLinkedShaders] = sh; - prog->_NumLinkedShaders++; + _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX], + sh); } if (num_frag_shaders > 0) { gl_shader *const sh = - link_intrastage_shaders(prog, frag_shader_list, num_frag_shaders); + link_intrastage_shaders(ctx, prog, frag_shader_list, num_frag_shaders); if (sh == NULL) goto done; if (!validate_fragment_shader_executable(prog, sh)) - goto done; + goto done; - prog->_LinkedShaders[prog->_NumLinkedShaders] = sh; - prog->_NumLinkedShaders++; + _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT], + sh); } /* Here begins the inter-stage linking phase. Some initial validation is @@ -1258,73 +1506,93 @@ link_shaders(struct gl_shader_program *prog) * varyings. */ if (cross_validate_uniforms(prog)) { + unsigned prev; + + for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { + if (prog->_LinkedShaders[prev] != NULL) + break; + } + /* Validate the inputs of each stage with the output of the preceeding * stage. */ - for (unsigned i = 1; i < prog->_NumLinkedShaders; i++) { + for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + if (!cross_validate_outputs_to_inputs(prog, - prog->_LinkedShaders[i - 1], + prog->_LinkedShaders[prev], prog->_LinkedShaders[i])) goto done; + + prev = i; } prog->LinkStatus = true; } - /* FINISHME: Perform whole-program optimization here. */ - for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { - /* Optimization passes */ - bool progress; - exec_list *ir = prog->_LinkedShaders[i]->ir; - - /* Lowering */ - do_mat_op_to_vec(ir); - do_mod_to_fract(ir); - do_div_to_mul_rcp(ir); - - do { - progress = false; - - progress = do_function_inlining(ir) || progress; - progress = do_if_simplification(ir) || progress; - progress = do_copy_propagation(ir) || progress; - progress = do_dead_code_local(ir) || progress; - progress = do_dead_code(ir) || progress; - progress = do_tree_grafting(ir) || progress; - progress = do_constant_variable(ir) || progress; - progress = do_constant_folding(ir) || progress; - progress = do_algebraic(ir) || progress; - progress = do_if_return(ir) || progress; -#if 0 - if (ctx->Shader.EmitNoIfs) - progress = do_if_to_cond_assign(ir) || progress; -#endif - - progress = do_vec_index_to_swizzle(ir) || progress; - /* Do this one after the previous to let the easier pass handle - * constant vector indexing. - */ - progress = do_vec_index_to_cond_assign(ir) || progress; + /* Do common optimization before assigning storage for attributes, + * uniforms, and varyings. Later optimization could possibly make + * some of that unused. + */ + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; - progress = do_swizzle_swizzle(ir) || progress; - } while (progress); + while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) + ; } + update_array_sizes(prog); + assign_uniform_locations(prog); - if (prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER) + 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)) + if (!assign_attribute_locations(prog, 16)) { + prog->LinkStatus = false; goto done; + } + } + + unsigned prev; + for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { + if (prog->_LinkedShaders[prev] != NULL) + break; + } + + for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; - for (unsigned i = 1; i < prog->_NumLinkedShaders; i++) assign_varying_locations(prog, - prog->_LinkedShaders[i - 1], + prog->_LinkedShaders[prev], prog->_LinkedShaders[i]); + prev = i; + } + + if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { + demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX], + ir_var_out); + } + + if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { + gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY]; + + 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); + } + + 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); + } /* FINISHME: Assign fragment shader output locations. */