diff options
Diffstat (limited to 'mesalib/src/glsl')
-rw-r--r-- | mesalib/src/glsl/ast_function.cpp | 414 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_parser.yy | 2 | ||||
-rw-r--r-- | mesalib/src/glsl/ir.h | 7 | ||||
-rw-r--r-- | mesalib/src/glsl/ir_function.cpp | 11 | ||||
-rw-r--r-- | mesalib/src/glsl/link_uniforms.cpp | 46 | ||||
-rw-r--r-- | mesalib/src/glsl/linker.cpp | 44 |
6 files changed, 335 insertions, 189 deletions
diff --git a/mesalib/src/glsl/ast_function.cpp b/mesalib/src/glsl/ast_function.cpp index fc0d7497d..126b610d1 100644 --- a/mesalib/src/glsl/ast_function.cpp +++ b/mesalib/src/glsl/ast_function.cpp @@ -93,214 +93,256 @@ prototype_string(const glsl_type *return_type, const char *name, return str; } - static ir_rvalue * -match_function_by_name(exec_list *instructions, const char *name, - YYLTYPE *loc, exec_list *actual_parameters, - struct _mesa_glsl_parse_state *state) +generate_call(exec_list *instructions, ir_function_signature *sig, + YYLTYPE *loc, exec_list *actual_parameters, + struct _mesa_glsl_parse_state *state) { void *ctx = state; - ir_function *f = state->symbols->get_function(name); - ir_function_signature *sig; - - sig = f ? f->matching_signature(actual_parameters) : NULL; + exec_list post_call_conversions; - /* FINISHME: This doesn't handle the case where shader X contains a - * FINISHME: matching signature but shader X + N contains an _exact_ - * FINISHME: matching signature. + /* Verify that 'out' and 'inout' actual parameters are lvalues. This + * isn't done in ir_function::matching_signature because that function + * cannot generate the necessary diagnostics. + * + * Also, validate that 'const_in' formal parameters (an extension of our + * IR) correspond to ir_constant actual parameters. + * + * Also, perform implicit conversion of arguments. Note: to implicitly + * convert out parameters, we need to place them in a temporary + * variable, and do the conversion after the call takes place. Since we + * haven't emitted the call yet, we'll place the post-call conversions + * in a temporary exec_list, and emit them later. */ - if (sig == NULL - && (f == NULL || state->es_shader || !f->has_user_signature()) - && state->symbols->get_type(name) == NULL - && (state->language_version == 110 - || state->symbols->get_variable(name) == NULL)) { - /* The current shader doesn't contain a matching function or signature. - * Before giving up, look for the prototype in the built-in functions. - */ - _mesa_glsl_initialize_functions(state); - for (unsigned i = 0; i < state->num_builtins_to_link; i++) { - ir_function *builtin; - builtin = state->builtins_to_link[i]->symbols->get_function(name); - sig = builtin ? builtin->matching_signature(actual_parameters) : NULL; - if (sig != NULL) { - if (f == NULL) { - f = new(ctx) ir_function(name); - state->symbols->add_global_function(f); - emit_function(state, f); - } + exec_list_iterator actual_iter = actual_parameters->iterator(); + exec_list_iterator formal_iter = sig->parameters.iterator(); + + while (actual_iter.has_next()) { + ir_rvalue *actual = (ir_rvalue *) actual_iter.get(); + ir_variable *formal = (ir_variable *) formal_iter.get(); - f->add_signature(sig->clone_prototype(f, NULL)); + assert(actual != NULL); + assert(formal != NULL); + + if (formal->mode == ir_var_const_in && !actual->as_constant()) { + _mesa_glsl_error(loc, state, + "parameter `%s' must be a constant expression", + formal->name); + return ir_call::get_error_instruction(ctx); + } + + if ((formal->mode == ir_var_out) + || (formal->mode == ir_var_inout)) { + const char *mode = NULL; + switch (formal->mode) { + case ir_var_out: mode = "out"; break; + case ir_var_inout: mode = "inout"; break; + default: assert(false); break; + } + /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always + * FIXME: 0:0(0). + */ + if (actual->variable_referenced() + && actual->variable_referenced()->read_only) { + _mesa_glsl_error(loc, state, + "function parameter '%s %s' references the " + "read-only variable '%s'", + mode, formal->name, + actual->variable_referenced()->name); + + } else if (!actual->is_lvalue()) { + _mesa_glsl_error(loc, state, + "function parameter '%s %s' is not an lvalue", + mode, formal->name); + } + } + + if (formal->type->is_numeric() || formal->type->is_boolean()) { + switch (formal->mode) { + case ir_var_const_in: + case ir_var_in: { + ir_rvalue *converted + = convert_component(actual, formal->type); + actual->replace_with(converted); + break; + } + case ir_var_out: + if (actual->type != formal->type) { + /* To convert an out parameter, we need to create a + * temporary variable to hold the value before conversion, + * and then perform the conversion after the function call + * returns. + * + * This has the effect of transforming code like this: + * + * void f(out int x); + * float value; + * f(value); + * + * Into IR that's equivalent to this: + * + * void f(out int x); + * float value; + * int out_parameter_conversion; + * f(out_parameter_conversion); + * value = float(out_parameter_conversion); + */ + ir_variable *tmp = + new(ctx) ir_variable(formal->type, + "out_parameter_conversion", + ir_var_temporary); + instructions->push_tail(tmp); + ir_dereference_variable *deref_tmp_1 + = new(ctx) ir_dereference_variable(tmp); + ir_dereference_variable *deref_tmp_2 + = new(ctx) ir_dereference_variable(tmp); + ir_rvalue *converted_tmp + = convert_component(deref_tmp_1, actual->type); + ir_assignment *assignment + = new(ctx) ir_assignment(actual, converted_tmp); + post_call_conversions.push_tail(assignment); + actual->replace_with(deref_tmp_2); + } + break; + case ir_var_inout: + /* Inout parameters should never require conversion, since that + * would require an implicit conversion to exist both to and + * from the formal parameter type, and there are no + * bidirectional implicit conversions. + */ + assert (actual->type == formal->type); + break; + default: + assert (!"Illegal formal parameter mode"); break; } } - } - exec_list post_call_conversions; + actual_iter.next(); + formal_iter.next(); + } - if (sig != NULL) { - /* Verify that 'out' and 'inout' actual parameters are lvalues. This - * isn't done in ir_function::matching_signature because that function - * cannot generate the necessary diagnostics. - * - * Also, validate that 'const_in' formal parameters (an extension of our - * IR) correspond to ir_constant actual parameters. + /* Always insert the call in the instruction stream, and return a deref + * of its return val if it returns a value, since we don't know if + * the rvalue is going to be assigned to anything or not. + * + * Also insert any out parameter conversions after the call. + */ + ir_call *call = new(ctx) ir_call(sig, actual_parameters); + ir_dereference_variable *deref; + if (!sig->return_type->is_void()) { + /* If the function call is a constant expression, don't + * generate the instructions to call it; just generate an + * ir_constant representing the constant value. * - * Also, perform implicit conversion of arguments. Note: to implicitly - * convert out parameters, we need to place them in a temporary - * variable, and do the conversion after the call takes place. Since we - * haven't emitted the call yet, we'll place the post-call conversions - * in a temporary exec_list, and emit them later. + * Function calls can only be constant expressions starting + * in GLSL 1.20. */ - exec_list_iterator actual_iter = actual_parameters->iterator(); - exec_list_iterator formal_iter = sig->parameters.iterator(); + if (state->language_version >= 120) { + ir_constant *const_val = call->constant_expression_value(); + if (const_val) { + return const_val; + } + } - while (actual_iter.has_next()) { - ir_rvalue *actual = (ir_rvalue *) actual_iter.get(); - ir_variable *formal = (ir_variable *) formal_iter.get(); + ir_variable *var; - assert(actual != NULL); - assert(formal != NULL); + var = new(ctx) ir_variable(sig->return_type, + ralloc_asprintf(ctx, "%s_retval", + sig->function_name()), + ir_var_temporary); + instructions->push_tail(var); - if (formal->mode == ir_var_const_in && !actual->as_constant()) { - _mesa_glsl_error(loc, state, - "parameter `%s' must be a constant expression", - formal->name); - return ir_call::get_error_instruction(ctx); - } + deref = new(ctx) ir_dereference_variable(var); + ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL); + instructions->push_tail(assign); - if ((formal->mode == ir_var_out) - || (formal->mode == ir_var_inout)) { - const char *mode = NULL; - switch (formal->mode) { - case ir_var_out: mode = "out"; break; - case ir_var_inout: mode = "inout"; break; - default: assert(false); break; - } - /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always - * FIXME: 0:0(0). - */ - if (actual->variable_referenced() - && actual->variable_referenced()->read_only) { - _mesa_glsl_error(loc, state, - "function parameter '%s %s' references the " - "read-only variable '%s'", - mode, formal->name, - actual->variable_referenced()->name); - - } else if (!actual->is_lvalue()) { - _mesa_glsl_error(loc, state, - "function parameter '%s %s' is not an lvalue", - mode, formal->name); - } - } + deref = new(ctx) ir_dereference_variable(var); + } else { + instructions->push_tail(call); + deref = NULL; + } + instructions->append_list(&post_call_conversions); + return deref; +} - if (formal->type->is_numeric() || formal->type->is_boolean()) { - switch (formal->mode) { - case ir_var_const_in: - case ir_var_in: { - ir_rvalue *converted - = convert_component(actual, formal->type); - actual->replace_with(converted); - break; - } - case ir_var_out: - if (actual->type != formal->type) { - /* To convert an out parameter, we need to create a - * temporary variable to hold the value before conversion, - * and then perform the conversion after the function call - * returns. - * - * This has the effect of transforming code like this: - * - * void f(out int x); - * float value; - * f(value); - * - * Into IR that's equivalent to this: - * - * void f(out int x); - * float value; - * int out_parameter_conversion; - * f(out_parameter_conversion); - * value = float(out_parameter_conversion); - */ - ir_variable *tmp = - new(ctx) ir_variable(formal->type, - "out_parameter_conversion", - ir_var_temporary); - instructions->push_tail(tmp); - ir_dereference_variable *deref_tmp_1 - = new(ctx) ir_dereference_variable(tmp); - ir_dereference_variable *deref_tmp_2 - = new(ctx) ir_dereference_variable(tmp); - ir_rvalue *converted_tmp - = convert_component(deref_tmp_1, actual->type); - ir_assignment *assignment - = new(ctx) ir_assignment(actual, converted_tmp); - post_call_conversions.push_tail(assignment); - actual->replace_with(deref_tmp_2); - } - break; - case ir_var_inout: - /* Inout parameters should never require conversion, since that - * would require an implicit conversion to exist both to and - * from the formal parameter type, and there are no - * bidirectional implicit conversions. - */ - assert (actual->type == formal->type); - break; - default: - assert (!"Illegal formal parameter mode"); - break; - } - } +static ir_rvalue * +match_function_by_name(exec_list *instructions, const char *name, + YYLTYPE *loc, exec_list *actual_parameters, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + ir_function *f = state->symbols->get_function(name); + ir_function_signature *local_sig = NULL; + ir_function_signature *sig = NULL; + + /* Is the function hidden by a record type constructor? */ + if (state->symbols->get_type(name)) + goto done; /* no match */ + + /* Is the function hidden by a variable (impossible in 1.10)? */ + if (state->language_version != 110 && state->symbols->get_variable(name)) + goto done; /* no match */ + + if (f != NULL) { + /* Look for a match in the local shader. If exact, we're done. */ + bool is_exact = false; + sig = local_sig = f->matching_signature(actual_parameters, &is_exact); + if (is_exact) + goto done; + + if (!state->es_shader && f->has_user_signature()) { + /* In desktop GL, the presence of a user-defined signature hides any + * built-in signatures, so we must ignore them. In contrast, in ES2 + * user-defined signatures add new overloads, so we must proceed. + */ + goto done; + } + } - actual_iter.next(); - formal_iter.next(); + /* Local shader has no exact candidates; check the built-ins. */ + _mesa_glsl_initialize_functions(state); + for (unsigned i = 0; i < state->num_builtins_to_link; i++) { + ir_function *builtin = + state->builtins_to_link[i]->symbols->get_function(name); + if (builtin == NULL) + continue; + + bool is_exact = false; + ir_function_signature *builtin_sig = + builtin->matching_signature(actual_parameters, &is_exact); + + if (builtin_sig == NULL) + continue; + + /* If the built-in signature is exact, we can stop. */ + if (is_exact) { + sig = builtin_sig; + goto done; } - /* Always insert the call in the instruction stream, and return a deref - * of its return val if it returns a value, since we don't know if - * the rvalue is going to be assigned to anything or not. - * - * Also insert any out parameter conversions after the call. - */ - ir_call *call = new(ctx) ir_call(sig, actual_parameters); - ir_dereference_variable *deref; - if (!sig->return_type->is_void()) { - /* If the function call is a constant expression, don't - * generate the instructions to call it; just generate an - * ir_constant representing the constant value. - * - * Function calls can only be constant expressions starting - * in GLSL 1.20. - */ - if (state->language_version >= 120) { - ir_constant *const_val = call->constant_expression_value(); - if (const_val) { - return const_val; - } - } - - ir_variable *var; - - var = new(ctx) ir_variable(sig->return_type, - ralloc_asprintf(ctx, "%s_retval", - sig->function_name()), - ir_var_temporary); - instructions->push_tail(var); - - deref = new(ctx) ir_dereference_variable(var); - ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL); - instructions->push_tail(assign); - - deref = new(ctx) ir_dereference_variable(var); - } else { - instructions->push_tail(call); - deref = NULL; + if (sig == NULL) { + /* We found an inexact match, which is better than nothing. However, + * we should keep searching for an exact match. + */ + sig = builtin_sig; + } + } + +done: + if (sig != NULL) { + /* If the match is from a linked built-in shader, import the prototype. */ + if (sig != local_sig) { + if (f == NULL) { + f = new(ctx) ir_function(name); + state->symbols->add_global_function(f); + emit_function(state, f); + } + f->add_signature(sig->clone_prototype(f, NULL)); } - instructions->append_list(&post_call_conversions); - return deref; + + /* Finally, generate a call instruction. */ + return generate_call(instructions, sig, loc, actual_parameters, state); } else { char *str = prototype_string(NULL, name, actual_parameters); diff --git a/mesalib/src/glsl/glsl_parser.yy b/mesalib/src/glsl/glsl_parser.yy index f3e873800..836390453 100644 --- a/mesalib/src/glsl/glsl_parser.yy +++ b/mesalib/src/glsl/glsl_parser.yy @@ -1706,7 +1706,7 @@ case_statement: ast_case_statement *stmts = new(state) ast_case_statement($1); stmts->stmts.push_tail(& $2->link); - $$ = stmts + $$ = stmts; } | case_statement statement { diff --git a/mesalib/src/glsl/ir.h b/mesalib/src/glsl/ir.h index 5878c051b..1faae3c72 100644 --- a/mesalib/src/glsl/ir.h +++ b/mesalib/src/glsl/ir.h @@ -566,6 +566,13 @@ public: /** * Find a signature that matches a set of actual parameters, taking implicit + * conversions into account. Also flags whether the match was exact. + */ + ir_function_signature *matching_signature(const exec_list *actual_param, + bool *match_is_exact); + + /** + * Find a signature that matches a set of actual parameters, taking implicit * conversions into account. */ ir_function_signature *matching_signature(const exec_list *actual_param); diff --git a/mesalib/src/glsl/ir_function.cpp b/mesalib/src/glsl/ir_function.cpp index 51d32b46f..b34a50081 100644 --- a/mesalib/src/glsl/ir_function.cpp +++ b/mesalib/src/glsl/ir_function.cpp @@ -118,6 +118,14 @@ parameter_lists_match(const exec_list *list_a, const exec_list *list_b) ir_function_signature * ir_function::matching_signature(const exec_list *actual_parameters) { + bool is_exact; + return matching_signature(actual_parameters, &is_exact); +} + +ir_function_signature * +ir_function::matching_signature(const exec_list *actual_parameters, + bool *is_exact) +{ ir_function_signature *match = NULL; bool multiple_inexact_matches = false; @@ -137,6 +145,7 @@ ir_function::matching_signature(const exec_list *actual_parameters) switch (parameter_lists_match(& sig->parameters, actual_parameters)) { case PARAMETER_LIST_EXACT_MATCH: + *is_exact = true; return sig; case PARAMETER_LIST_INEXACT_MATCH: if (match == NULL) @@ -159,6 +168,8 @@ ir_function::matching_signature(const exec_list *actual_parameters) * FINISHME: a "no matching signature" error; it should report that the * FINISHME: call is ambiguous. But reporting errors from here is hard. */ + *is_exact = false; + if (multiple_inexact_matches) return NULL; diff --git a/mesalib/src/glsl/link_uniforms.cpp b/mesalib/src/glsl/link_uniforms.cpp index b9d5361b0..ca5da3040 100644 --- a/mesalib/src/glsl/link_uniforms.cpp +++ b/mesalib/src/glsl/link_uniforms.cpp @@ -113,11 +113,18 @@ uniform_field_visitor::recursion(const glsl_type *t, char **name, class count_uniform_size : public uniform_field_visitor { public: count_uniform_size(struct string_to_uint_map *map) - : num_active_uniforms(0), num_values(0), map(map) + : num_active_uniforms(0), num_values(0), num_shader_samplers(0), + num_shader_uniforms(0), map(map) { /* empty */ } + void start_shader() + { + this->num_shader_samplers = 0; + this->num_shader_uniforms = 0; + } + /** * Total number of active uniforms counted */ @@ -128,12 +135,39 @@ public: */ unsigned num_values; + /** + * Number of samplers used + */ + unsigned num_shader_samplers; + + /** + * Number of uniforms used in the current shader + */ + unsigned num_shader_uniforms; + private: virtual void visit_field(const glsl_type *type, const char *name) { assert(!type->is_record()); assert(!(type->is_array() && type->fields.array->is_record())); + /* Count the number of samplers regardless of whether the uniform is + * already in the hash table. The hash table prevents adding the same + * uniform for multiple shader targets, but in this case we want to + * count it for each shader target. + */ + const unsigned values = values_for_type(type); + if (type->contains_sampler()) { + this->num_shader_samplers += + type->is_array() ? type->array_size() : 1; + } else { + /* Accumulate the total number of uniform slots used by this shader. + * Note that samplers do not count against this limit because they + * don't use any storage on current hardware. + */ + this->num_shader_uniforms += values; + } + /* If the uniform is already in the map, there's nothing more to do. */ unsigned id; @@ -147,7 +181,7 @@ private: * uniforms. */ this->num_active_uniforms++; - this->num_values += values_for_type(type); + this->num_values += values; } struct string_to_uint_map *map; @@ -267,6 +301,10 @@ link_assign_uniform_locations(struct gl_shader_program *prog) if (prog->_LinkedShaders[i] == NULL) continue; + /* Reset various per-shader target counts. + */ + uniform_size.start_shader(); + foreach_list(node, prog->_LinkedShaders[i]->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); @@ -280,6 +318,10 @@ link_assign_uniform_locations(struct gl_shader_program *prog) uniform_size.process(var); } + + prog->_LinkedShaders[i]->num_samplers = uniform_size.num_shader_samplers; + prog->_LinkedShaders[i]->num_uniform_components = + uniform_size.num_shader_uniforms * 4; } const unsigned num_user_uniforms = uniform_size.num_active_uniforms; diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index 351680d43..0ec773d6c 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -1875,6 +1875,47 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog, return true; } +/** + * 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]) { + 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) @@ -2137,6 +2178,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) update_array_sizes(prog); link_assign_uniform_locations(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. |