X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flinker.cpp;h=c71c07d6e97fef3912f62757e2ebc6a9bbdc4f0a;hb=87a2ee8db6222006480bd0e0ac58b77795c5d951;hp=e70fa31a2b069f66af74d901134d4e9a35a7b2b1;hpb=3fb878722ed53d79eedb9fe68972ef32b79575d4;p=mesa.git diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index e70fa31a2b0..c71c07d6e97 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -242,42 +242,72 @@ validate_fragment_shader_executable(struct gl_shader_program *prog, /** - * Perform validation of uniforms used across multiple shader stages + * Generate a string describing the mode of a variable + */ +static const char * +mode_string(const ir_variable *var) +{ + switch (var->mode) { + case ir_var_auto: + return (var->read_only) ? "global constant" : "global variable"; + + case ir_var_uniform: return "uniform"; + case ir_var_in: return "shader input"; + case ir_var_out: return "shader output"; + case ir_var_inout: return "shader inout"; + default: + assert(!"Should not get here."); + return "invalid variable"; + } +} + + +/** + * Perform validation of global variables used across multiple shaders */ bool -cross_validate_uniforms(struct gl_shader_program *prog) +cross_validate_globals(struct gl_shader_program *prog, + struct gl_shader **shader_list, + unsigned num_shaders, + bool uniforms_only) { /* Examine all of the uniforms in all of the shaders and cross validate * them. */ - glsl_symbol_table uniforms; - for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { - foreach_list(node, prog->_LinkedShaders[i]->ir) { + glsl_symbol_table variables; + for (unsigned i = 0; i < num_shaders; i++) { + foreach_list(node, shader_list[i]->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); - if ((var == NULL) || (var->mode != ir_var_uniform)) + if (var == NULL) continue; - /* If a uniform with this name has already been seen, verify that the - * new instance has the same type. In addition, if the uniforms have + if (uniforms_only && (var->mode != ir_var_uniform)) + continue; + + /* If a global with this name has already been seen, verify that the + * new instance has the same type. In addition, if the globals have * initializers, the values of the initializers must be the same. */ - ir_variable *const existing = uniforms.get_variable(var->name); + ir_variable *const existing = variables.get_variable(var->name); if (existing != NULL) { if (var->type != existing->type) { - linker_error_printf(prog, "uniform `%s' declared as 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; } + /* FINISHME: Handle non-constant initializers. + */ 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 uniform " + linker_error_printf(prog, "initializers for %s " "`%s' have differing values\n", - var->name); + mode_string(var), var->name); return false; } } else @@ -285,11 +315,10 @@ cross_validate_uniforms(struct gl_shader_program *prog) * have an initializer but a later instance does, copy the * initializer to the version stored in the symbol table. */ - existing->constant_value = - (ir_constant *)var->constant_value->clone(NULL); + existing->constant_value = var->constant_value->clone(NULL); } } else - uniforms.add_variable(var->name, var); + variables.add_variable(var->name, var); } } @@ -297,6 +326,17 @@ cross_validate_uniforms(struct gl_shader_program *prog) } +/** + * Perform validation of uniforms used across multiple shader stages + */ +bool +cross_validate_uniforms(struct gl_shader_program *prog) +{ + return cross_validate_globals(prog, prog->_LinkedShaders, + prog->_NumLinkedShaders, true); +} + + /** * Validate that outputs from one stage match inputs of another */ @@ -420,6 +460,138 @@ populate_symbol_table(gl_shader *sh) } +/** + * Remap variables referenced in an instruction tree + * + * This is used when instruction trees are cloned from one shader and placed in + * another. These trees will contain references to \c ir_variable nodes that + * do not exist in the target shader. This function finds these \c ir_variable + * references and replaces the references with matching variables in the target + * shader. + * + * If there is no matching variable in the target shader, a clone of the + * \c ir_variable is made and added to the target shader. The new variable is + * added to \b both the instruction stream and the symbol table. + * + * \param inst IR tree that is to be processed. + * \param symbols Symbol table containing global scope symbols in the + * linked shader. + * \param instructions Instruction stream where new variable declarations + * should be added. + */ +void +remap_variables(ir_instruction *inst, glsl_symbol_table *symbols, + exec_list *instructions) +{ + class remap_visitor : public ir_hierarchical_visitor { + public: + remap_visitor(glsl_symbol_table *symbols, exec_list *instructions) + { + this->symbols = symbols; + this->instructions = instructions; + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + ir_variable *const existing = + this->symbols->get_variable(ir->var->name); + if (existing != NULL) + ir->var = existing; + else { + ir_variable *copy = ir->var->clone(NULL); + + this->symbols->add_variable(copy->name, copy); + this->instructions->push_head(copy); + } + + return visit_continue; + } + + private: + glsl_symbol_table *symbols; + exec_list *instructions; + }; + + remap_visitor v(symbols, instructions); + + inst->accept(&v); +} + + +/** + * Move non-declarations from one instruction stream to another + * + * The intended usage pattern of this function is to pass the pointer to the + * head sentinal of a list (i.e., a pointer to the list cast to an \c exec_node + * pointer) for \c last and \c false for \c make_copies on the first + * call. Successive calls pass the return value of the previous call for + * \c last and \c true for \c make_copies. + * + * \param instructions Source instruction stream + * \param last Instruction after which new instructions should be + * inserted in the target instruction stream + * \param make_copies Flag selecting whether instructions in \c instructions + * should be copied (via \c ir_instruction::clone) into the + * target list or moved. + * + * \return + * The new "last" instruction in the target instruction stream. This pointer + * is suitable for use as the \c last parameter of a later call to this + * function. + */ +exec_node * +move_non_declarations(exec_list *instructions, exec_node *last, + bool make_copies, gl_shader *target) +{ + foreach_list(node, instructions) { + ir_instruction *inst = (ir_instruction *) node; + + if (inst->as_variable() || inst->as_function()) + continue; + + assert(inst->as_assignment()); + + if (make_copies) { + inst = inst->clone(NULL); + remap_variables(inst, target->symbols, target->ir); + } else { + inst->remove(); + } + + last->insert_after(inst); + last = inst; + } + + return last; +} + +/** + * Get the function signature for main from a shader + */ +static ir_function_signature * +get_main_function_signature(gl_shader *sh) +{ + ir_function *const f = sh->symbols->get_function("main"); + if (f != NULL) { + exec_list void_parameters; + + /* Look for the 'void main()' signature and ensure that it's defined. + * This keeps the linker from accidentally pick a shader that just + * contains a prototype for main. + * + * We don't have to check for multiple definitions of main (in multiple + * shaders) because that would have already been caught above. + */ + ir_function_signature *sig = f->matching_signature(&void_parameters); + if ((sig != NULL) && sig->is_defined) { + return sig; + } + } + + return NULL; +} + + /** * Combine a group of shaders for a single stage to generate a linked shader * @@ -432,15 +604,100 @@ link_intrastage_shaders(struct gl_shader_program *prog, struct gl_shader **shader_list, unsigned num_shaders) { - (void) prog; - assert(num_shaders == 1); + /* Check that global variables defined in multiple shaders are consistent. + */ + if (!cross_validate_globals(prog, shader_list, num_shaders, false)) + return NULL; + + /* Check that there is only a single definition of each function signature + * across all shaders. + */ + for (unsigned i = 0; i < (num_shaders - 1); i++) { + foreach_list(node, shader_list[i]->ir) { + ir_function *const f = ((ir_instruction *) node)->as_function(); + + if (f == NULL) + continue; + + for (unsigned j = i + 1; j < num_shaders; j++) { + ir_function *const other = + shader_list[j]->symbols->get_function(f->name); + + /* If the other shader has no function (and therefore no function + * signatures) with the same name, skip to the next shader. + */ + if (other == NULL) + continue; + + foreach_iter (exec_list_iterator, iter, *f) { + ir_function_signature *sig = + (ir_function_signature *) iter.get(); + + if (!sig->is_defined || sig->is_built_in) + 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) { + linker_error_printf(prog, + "function `%s' is multiply defined", + f->name); + return NULL; + } + } + } + } + } + + /* Find the shader that defines main, and make a clone of it. + * + * Starting with the clone, search for undefined references. If one is + * found, find the shader that defines it. Clone the reference and add + * it to the shader. Repeat until there are no undefined references or + * until a reference cannot be resolved. + */ + gl_shader *main = NULL; + for (unsigned i = 0; i < num_shaders; i++) { + if (get_main_function_signature(shader_list[i]) != NULL) { + main = shader_list[i]; + break; + } + } + + if (main == NULL) { + linker_error_printf(prog, "%s shader lacks `main'\n", + (shader_list[0]->Type == GL_VERTEX_SHADER) + ? "vertex" : "fragment"); + return NULL; + } - gl_shader *const linked = _mesa_new_shader(NULL, 0, shader_list[0]->Type); + gl_shader *const linked = _mesa_new_shader(NULL, 0, main->Type); linked->ir = new(linked) exec_list; - clone_ir_list(linked->ir, shader_list[0]->ir); + clone_ir_list(linked->ir, main->ir); populate_symbol_table(linked); + /* The a pointer to the main function in the final linked shader (i.e., the + * copy of the original shader that contained the main function). + */ + ir_function_signature *const main_sig = get_main_function_signature(linked); + + /* Move any instructions other than variable declarations or function + * declarations into main. + */ + exec_node *insertion_point = (exec_node *) &main_sig->body; + for (unsigned i = 0; i < num_shaders; i++) { + insertion_point = move_non_declarations(shader_list[i]->ir, + insertion_point, + (shader_list[i] != main), + linked); + } + + /* Resolve initializers for global variables in the linked shader. + */ + return linked; } @@ -795,7 +1052,10 @@ assign_varying_locations(gl_shader *producer, gl_shader *consumer) /* An 'out' variable is only really a shader output if its value is read * by the following stage. */ - var->shader_out = (var->location != -1); + if (var->location == -1) { + var->shader_out = false; + var->mode = ir_var_auto; + } } foreach_list(node, consumer->ir) {