diff options
Diffstat (limited to 'mesalib/src/glsl')
-rw-r--r-- | mesalib/src/glsl/ast.h | 83 | ||||
-rw-r--r-- | mesalib/src/glsl/ast_to_hir.cpp | 310 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_parser.yy | 87 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_parser_extras.cpp | 103 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_parser_extras.h | 10 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_types.h | 19 | ||||
-rw-r--r-- | mesalib/src/glsl/ir_uniform.h | 128 | ||||
-rw-r--r-- | mesalib/src/glsl/link_uniforms.cpp | 249 | ||||
-rw-r--r-- | mesalib/src/glsl/linker.cpp | 180 | ||||
-rw-r--r-- | mesalib/src/glsl/linker.h | 7 |
10 files changed, 968 insertions, 208 deletions
diff --git a/mesalib/src/glsl/ast.h b/mesalib/src/glsl/ast.h index 9fe6c4125..d899bc62a 100644 --- a/mesalib/src/glsl/ast.h +++ b/mesalib/src/glsl/ast.h @@ -629,13 +629,78 @@ public: class ast_case_label : public ast_node { public: + ast_case_label(ast_expression *test_value); + virtual void print(void) const; + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); /** - * An expression of NULL means 'default'. + * An test value of NULL means 'default'. */ - ast_expression *expression; + ast_expression *test_value; +}; + + +class ast_case_label_list : public ast_node { +public: + ast_case_label_list(void); + virtual void print(void) const; + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + + /** + * A list of case labels. + */ + exec_list labels; }; + +class ast_case_statement : public ast_node { +public: + ast_case_statement(ast_case_label_list *labels); + virtual void print(void) const; + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + + ast_case_label_list *labels; + + /** + * A list of statements. + */ + exec_list stmts; +}; + + +class ast_case_statement_list : public ast_node { +public: + ast_case_statement_list(void); + virtual void print(void) const; + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + + /** + * A list of cases. + */ + exec_list cases; +}; + + +class ast_switch_body : public ast_node { +public: + ast_switch_body(ast_case_statement_list *stmts); + virtual void print(void) const; + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + + ast_case_statement_list *stmts; +}; + + class ast_selection_statement : public ast_node { public: ast_selection_statement(ast_expression *condition, @@ -654,8 +719,18 @@ public: class ast_switch_statement : public ast_node { public: - ast_expression *expression; - exec_list statements; + ast_switch_statement(ast_expression *test_expression, + ast_node *body); + virtual void print(void) const; + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + + ast_expression *test_expression; + ast_node *body; + +protected: + void test_to_hir(exec_list *, struct _mesa_glsl_parse_state *); }; class ast_iteration_statement : public ast_node { diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp index ed6abdc70..ac090c315 100644 --- a/mesalib/src/glsl/ast_to_hir.cpp +++ b/mesalib/src/glsl/ast_to_hir.cpp @@ -3355,34 +3355,49 @@ ast_jump_statement::hir(exec_list *instructions, case ast_break: case ast_continue: - /* FINISHME: Handle switch-statements. They cannot contain 'continue', - * FINISHME: and they use a different IR instruction for 'break'. - */ - /* FINISHME: Correctly handle the nesting. If a switch-statement is - * FINISHME: inside a loop, a 'continue' is valid and will bind to the - * FINISHME: loop. - */ - if (state->loop_or_switch_nesting == NULL) { + if (mode == ast_continue && + state->loop_nesting_ast == NULL) { YYLTYPE loc = this->get_location(); _mesa_glsl_error(& loc, state, - "`%s' may only appear in a loop", - (mode == ast_break) ? "break" : "continue"); - } else { - ir_loop *const loop = state->loop_or_switch_nesting->as_loop(); + "continue may only appear in a loop"); + } else if (mode == ast_break && + state->loop_nesting_ast == NULL && + state->switch_nesting_ast == NULL) { + YYLTYPE loc = this->get_location(); - /* Inline the for loop expression again, since we don't know - * where near the end of the loop body the normal copy of it + _mesa_glsl_error(& loc, state, + "break may only appear in a loop or a switch"); + } else { + /* For a loop, inline the for loop expression again, + * since we don't know where near the end of + * the loop body the normal copy of it * is going to be placed. */ - if (mode == ast_continue && - state->loop_or_switch_nesting_ast->rest_expression) { - state->loop_or_switch_nesting_ast->rest_expression->hir(instructions, - state); + if (state->loop_nesting_ast != NULL && + mode == ast_continue && + state->loop_nesting_ast->rest_expression) { + state->loop_nesting_ast->rest_expression->hir(instructions, + state); } - if (loop != NULL) { - ir_loop_jump *const jump = + if (state->is_switch_innermost && + mode == ast_break) { + /* Force break out of switch by setting is_break switch state. + */ + ir_variable *const is_break_var = state->is_break_var; + ir_dereference_variable *const deref_is_break_var = + new(ctx) ir_dereference_variable(is_break_var); + ir_constant *const true_val = new(ctx) ir_constant(true); + ir_assignment *const set_break_var = + new(ctx) ir_assignment(deref_is_break_var, + true_val, + NULL); + + instructions->push_tail(set_break_var); + } + else { + ir_loop_jump *const jump = new(ctx) ir_loop_jump((mode == ast_break) ? ir_loop_jump::jump_break : ir_loop_jump::jump_continue); @@ -3445,6 +3460,243 @@ ast_selection_statement::hir(exec_list *instructions, } +ir_rvalue * +ast_switch_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + ir_rvalue *const test_expression = + this->test_expression->hir(instructions, state); + + /* From page 66 (page 55 of the PDF) of the GLSL 1.50 spec: + * + * "The type of init-expression in a switch statement must be a + * scalar integer." + * + * The checks are separated so that higher quality diagnostics can be + * generated for cases where the rule is violated. + */ + if (!test_expression->type->is_integer()) { + YYLTYPE loc = this->test_expression->get_location(); + + _mesa_glsl_error(& loc, + state, + "switch-statement expression must be scalar " + "integer"); + } + + /* Track the switch-statement nesting in a stack-like manner. + */ + ir_variable *saved_test_var = state->test_var; + ir_variable *saved_is_fallthru_var = state->is_fallthru_var; + + bool save_is_switch_innermost = state->is_switch_innermost; + ast_switch_statement *saved_nesting_ast = state->switch_nesting_ast; + + state->is_switch_innermost = true; + state->switch_nesting_ast = this; + + /* Initalize is_fallthru state to false. + */ + ir_rvalue *const is_fallthru_val = new (ctx) ir_constant(false); + state->is_fallthru_var = new(ctx) ir_variable(glsl_type::bool_type, + "switch_is_fallthru_tmp", + ir_var_temporary); + instructions->push_tail(state->is_fallthru_var); + + ir_dereference_variable *deref_is_fallthru_var = + new(ctx) ir_dereference_variable(state->is_fallthru_var); + instructions->push_tail(new(ctx) ir_assignment(deref_is_fallthru_var, + is_fallthru_val, + NULL)); + + /* Initalize is_break state to false. + */ + ir_rvalue *const is_break_val = new (ctx) ir_constant(false); + state->is_break_var = new(ctx) ir_variable(glsl_type::bool_type, + "switch_is_break_tmp", + ir_var_temporary); + instructions->push_tail(state->is_break_var); + + ir_dereference_variable *deref_is_break_var = + new(ctx) ir_dereference_variable(state->is_break_var); + instructions->push_tail(new(ctx) ir_assignment(deref_is_break_var, + is_break_val, + NULL)); + + /* Cache test expression. + */ + test_to_hir(instructions, state); + + /* Emit code for body of switch stmt. + */ + body->hir(instructions, state); + + /* Restore previous nesting before returning. + */ + state->switch_nesting_ast = saved_nesting_ast; + state->is_switch_innermost = save_is_switch_innermost; + + state->test_var = saved_test_var; + state->is_fallthru_var = saved_is_fallthru_var; + + /* Switch statements do not have r-values. + */ + return NULL; +} + + +void +ast_switch_statement::test_to_hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + /* Cache value of test expression. + */ + ir_rvalue *const test_val = + test_expression->hir(instructions, + state); + + state->test_var = new(ctx) ir_variable(glsl_type::int_type, + "switch_test_tmp", + ir_var_temporary); + ir_dereference_variable *deref_test_var = + new(ctx) ir_dereference_variable(state->test_var); + + instructions->push_tail(state->test_var); + instructions->push_tail(new(ctx) ir_assignment(deref_test_var, + test_val, + NULL)); +} + + +ir_rvalue * +ast_switch_body::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + if (stmts != NULL) + stmts->hir(instructions, state); + + /* Switch bodies do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_case_statement_list::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + foreach_list_typed (ast_case_statement, case_stmt, link, & this->cases) + case_stmt->hir(instructions, state); + + /* Case statements do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_case_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + labels->hir(instructions, state); + + /* Conditionally set fallthru state based on break state. + */ + ir_constant *const false_val = new(state) ir_constant(false); + ir_dereference_variable *const deref_is_fallthru_var = + new(state) ir_dereference_variable(state->is_fallthru_var); + ir_dereference_variable *const deref_is_break_var = + new(state) ir_dereference_variable(state->is_break_var); + ir_assignment *const reset_fallthru_on_break = + new(state) ir_assignment(deref_is_fallthru_var, + false_val, + deref_is_break_var); + instructions->push_tail(reset_fallthru_on_break); + + /* Guard case statements depending on fallthru state. + */ + ir_dereference_variable *const deref_fallthru_guard = + new(state) ir_dereference_variable(state->is_fallthru_var); + ir_if *const test_fallthru = new(state) ir_if(deref_fallthru_guard); + + foreach_list_typed (ast_node, stmt, link, & this->stmts) + stmt->hir(& test_fallthru->then_instructions, state); + + instructions->push_tail(test_fallthru); + + /* Case statements do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_case_label_list::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + foreach_list_typed (ast_case_label, label, link, & this->labels) + label->hir(instructions, state); + + /* Case labels do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_case_label::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + ir_dereference_variable *deref_fallthru_var = + new(ctx) ir_dereference_variable(state->is_fallthru_var); + + ir_rvalue *const true_val = new(ctx) ir_constant(true); + + /* If not default case, ... + */ + if (this->test_value != NULL) { + /* Conditionally set fallthru state based on + * comparison of cached test expression value to case label. + */ + ir_rvalue *const test_val = this->test_value->hir(instructions, state); + + ir_dereference_variable *deref_test_var = + new(ctx) ir_dereference_variable(state->test_var); + + ir_rvalue *const test_cond = new(ctx) ir_expression(ir_binop_all_equal, + glsl_type::bool_type, + test_val, + deref_test_var); + + ir_assignment *set_fallthru_on_test = + new(ctx) ir_assignment(deref_fallthru_var, + true_val, + test_cond); + + instructions->push_tail(set_fallthru_on_test); + } else { /* default case */ + /* Set falltrhu state. + */ + ir_assignment *set_fallthru = + new(ctx) ir_assignment(deref_fallthru_var, + true_val, + NULL); + + instructions->push_tail(set_fallthru); + } + + /* Case statements do not have r-values. + */ + return NULL; +} + + void ast_iteration_statement::condition_to_hir(ir_loop *stmt, struct _mesa_glsl_parse_state *state) @@ -3498,13 +3750,17 @@ ast_iteration_statement::hir(exec_list *instructions, ir_loop *const stmt = new(ctx) ir_loop(); instructions->push_tail(stmt); - /* Track the current loop and / or switch-statement nesting. + /* Track the current loop nesting. */ - ir_instruction *const nesting = state->loop_or_switch_nesting; - ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast; + ast_iteration_statement *nesting_ast = state->loop_nesting_ast; + + state->loop_nesting_ast = this; - state->loop_or_switch_nesting = stmt; - state->loop_or_switch_nesting_ast = this; + /* Likewise, indicate that following code is closest to a loop, + * NOT closest to a switch. + */ + bool saved_is_switch_innermost = state->is_switch_innermost; + state->is_switch_innermost = false; if (mode != ast_do_while) condition_to_hir(stmt, state); @@ -3523,8 +3779,8 @@ ast_iteration_statement::hir(exec_list *instructions, /* Restore previous nesting before returning. */ - state->loop_or_switch_nesting = nesting; - state->loop_or_switch_nesting_ast = nesting_ast; + state->loop_nesting_ast = nesting_ast; + state->is_switch_innermost = saved_is_switch_innermost; /* Loops do not have r-values. */ diff --git a/mesalib/src/glsl/glsl_parser.yy b/mesalib/src/glsl/glsl_parser.yy index d32d6e4e1..f3e873800 100644 --- a/mesalib/src/glsl/glsl_parser.yy +++ b/mesalib/src/glsl/glsl_parser.yy @@ -67,6 +67,11 @@ ast_declarator_list *declarator_list; ast_struct_specifier *struct_specifier; ast_declaration *declaration; + ast_switch_body *switch_body; + ast_case_label *case_label; + ast_case_label_list *case_label_list; + ast_case_statement *case_statement; + ast_case_statement_list *case_statement_list; struct { ast_node *cond; @@ -207,6 +212,12 @@ %type <declaration> struct_declarator_list %type <node> selection_statement %type <selection_rest_statement> selection_rest_statement +%type <node> switch_statement +%type <switch_body> switch_body +%type <case_label_list> case_label_list +%type <case_label> case_label +%type <case_statement> case_statement +%type <case_statement_list> case_statement_list %type <node> iteration_statement %type <node> condition %type <node> conditionopt @@ -1517,8 +1528,7 @@ simple_statement: declaration_statement | expression_statement | selection_statement - | switch_statement { $$ = NULL; } - | case_label { $$ = NULL; } + | switch_statement | iteration_statement | jump_statement ; @@ -1640,13 +1650,84 @@ condition: } ; +/* + * siwtch_statement grammar is based on the syntax described in the body + * of the GLSL spec, not in it's appendix!!! + */ switch_statement: - SWITCH '(' expression ')' compound_statement + SWITCH '(' expression ')' switch_body + { + $$ = new(state) ast_switch_statement($3, $5); + } + ; + +switch_body: + '{' '}' + { + $$ = new(state) ast_switch_body(NULL); + $$->set_location(yylloc); + } + | '{' case_statement_list '}' + { + $$ = new(state) ast_switch_body($2); + $$->set_location(yylloc); + } ; case_label: CASE expression ':' + { + $$ = new(state) ast_case_label($2); + } | DEFAULT ':' + { + $$ = new(state) ast_case_label(NULL); + } + ; + +case_label_list: + case_label + { + ast_case_label_list *labels = new(state) ast_case_label_list(); + + labels->labels.push_tail(& $1->link); + $$ = labels; + } + | case_label_list case_label + { + $$ = $1; + $$->labels.push_tail(& $2->link); + } + ; + +case_statement: + case_label_list statement + { + ast_case_statement *stmts = new(state) ast_case_statement($1); + + stmts->stmts.push_tail(& $2->link); + $$ = stmts + } + | case_statement statement + { + $$ = $1; + $$->stmts.push_tail(& $2->link); + } + ; + +case_statement_list: + case_statement + { + ast_case_statement_list *cases= new(state) ast_case_statement_list(); + + cases->cases.push_tail(& $1->link); + $$ = cases; + } + | case_statement_list case_statement + { + $$ = $1; + $$->cases.push_tail(& $2->link); + } ; iteration_statement: diff --git a/mesalib/src/glsl/glsl_parser_extras.cpp b/mesalib/src/glsl/glsl_parser_extras.cpp index e627dabf7..23aadb143 100644 --- a/mesalib/src/glsl/glsl_parser_extras.cpp +++ b/mesalib/src/glsl/glsl_parser_extras.cpp @@ -50,7 +50,8 @@ _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx, this->symbols = new(mem_ctx) glsl_symbol_table; this->info_log = ralloc_strdup(mem_ctx, ""); this->error = false; - this->loop_or_switch_nesting = NULL; + this->loop_nesting_ast = NULL; + this->switch_nesting_ast = NULL; this->num_builtins_to_link = 0; @@ -806,6 +807,106 @@ ast_selection_statement::ast_selection_statement(ast_expression *condition, void +ast_switch_statement::print(void) const +{ + printf("switch ( "); + test_expression->print(); + printf(") "); + + body->print(); +} + + +ast_switch_statement::ast_switch_statement(ast_expression *test_expression, + ast_node *body) +{ + this->test_expression = test_expression; + this->body = body; +} + + +void +ast_switch_body::print(void) const +{ + printf("{\n"); + if (stmts != NULL) { + stmts->print(); + } + printf("}\n"); +} + + +ast_switch_body::ast_switch_body(ast_case_statement_list *stmts) +{ + this->stmts = stmts; +} + + +void ast_case_label::print(void) const +{ + if (test_value != NULL) { + printf("case "); + test_value->print(); + printf(": "); + } else { + printf("default: "); + } +} + + +ast_case_label::ast_case_label(ast_expression *test_value) +{ + this->test_value = test_value; +} + + +void ast_case_label_list::print(void) const +{ + foreach_list_const(n, & this->labels) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + printf("\n"); +} + + +ast_case_label_list::ast_case_label_list(void) +{ +} + + +void ast_case_statement::print(void) const +{ + labels->print(); + foreach_list_const(n, & this->stmts) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + printf("\n"); + } +} + + +ast_case_statement::ast_case_statement(ast_case_label_list *labels) +{ + this->labels = labels; +} + + +void ast_case_statement_list::print(void) const +{ + foreach_list_const(n, & this->cases) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } +} + + +ast_case_statement_list::ast_case_statement_list(void) +{ +} + + +void ast_iteration_statement::print(void) const { switch (mode) { diff --git a/mesalib/src/glsl/glsl_parser_extras.h b/mesalib/src/glsl/glsl_parser_extras.h index 1f3404c9d..dd932951f 100644 --- a/mesalib/src/glsl/glsl_parser_extras.h +++ b/mesalib/src/glsl/glsl_parser_extras.h @@ -149,8 +149,14 @@ struct _mesa_glsl_parse_state { bool all_invariant; /** Loop or switch statement containing the current instructions. */ - class ir_instruction *loop_or_switch_nesting; - class ast_iteration_statement *loop_or_switch_nesting_ast; + class ast_iteration_statement *loop_nesting_ast; + class ast_switch_statement *switch_nesting_ast; + bool is_switch_innermost; // if switch stmt is closest to break, ... + + /** Temporary variables needed for switch statement. */ + ir_variable *test_var; + ir_variable *is_fallthru_var; + ir_variable *is_break_var; /** List of structures defined in user code. */ const glsl_type **user_structures; diff --git a/mesalib/src/glsl/glsl_types.h b/mesalib/src/glsl/glsl_types.h index 56b8efe7b..4ac90118b 100644 --- a/mesalib/src/glsl/glsl_types.h +++ b/mesalib/src/glsl/glsl_types.h @@ -29,21 +29,23 @@ #include <string.h> #include <assert.h> +#ifdef __cplusplus extern "C" { -#include "GL/gl.h" -} - -#include "ralloc.h" +#endif struct _mesa_glsl_parse_state; struct glsl_symbol_table; -extern "C" void +extern void _mesa_glsl_initialize_types(struct _mesa_glsl_parse_state *state); -extern "C" void +extern void _mesa_glsl_release_types(void); +#ifdef __cplusplus +} +#endif + enum glsl_base_type { GLSL_TYPE_UINT = 0, GLSL_TYPE_INT, @@ -66,6 +68,9 @@ enum glsl_sampler_dim { GLSL_SAMPLER_DIM_EXTERNAL }; +#ifdef __cplusplus +#include "GL/gl.h" +#include "ralloc.h" struct glsl_type { GLenum gl_type; @@ -530,4 +535,6 @@ struct glsl_struct_field { const char *name; }; +#endif /* __cplusplus */ + #endif /* GLSL_TYPES_H */ diff --git a/mesalib/src/glsl/ir_uniform.h b/mesalib/src/glsl/ir_uniform.h new file mode 100644 index 000000000..225da3fc5 --- /dev/null +++ b/mesalib/src/glsl/ir_uniform.h @@ -0,0 +1,128 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef IR_UNIFORM_H +#define IR_UNIFORM_H + + +/* stdbool.h is necessary because this file is included in both C and C++ code. + */ +#include <stdbool.h> + +#include "program/prog_parameter.h" /* For union gl_constant_value. */ + + +#ifdef __cplusplus +extern "C" { +#endif + +enum gl_uniform_driver_format { + uniform_native = 0, /**< Store data in the native format. */ + uniform_int_float, /**< Store integer data as floats. */ + uniform_bool_float, /**< Store boolean data as floats. */ + + /** + * Store boolean data as integer using 1 for \c true. + */ + uniform_bool_int_0_1, + + /** + * Store boolean data as integer using ~0 for \c true. + */ + uniform_bool_int_0_not0 +}; + +struct gl_uniform_driver_storage { + /** + * Number of bytes from one array element to the next. + */ + uint8_t element_stride; + + /** + * Number of bytes from one vector in a matrix to the next. + */ + uint8_t vector_stride; + + /** + * Base format of the stored data. + * + * This field must have a value from \c GLSL_TYPE_UINT through \c + * GLSL_TYPE_SAMPLER. + */ + uint8_t format; + + /** + * Pointer to the base of the data. + */ + void *data; +}; + +struct gl_uniform_storage { + char *name; + const struct glsl_type *type; + + /** + * The number of elements in this uniform. + * + * For non-arrays, this is always 0. For arrays, the value is the size of + * the array. + */ + unsigned array_elements; + + /** + * Has this uniform ever been set? + */ + bool initialized; + + /** + * Base sampler index + * + * If \c ::base_type is \c GLSL_TYPE_SAMPLER, this represents the index of + * this sampler. If \c ::array_elements is not zero, the array will use + * sampler indexes \c ::sampler through \c ::sampler + \c ::array_elements + * - 1, inclusive. + */ + uint8_t sampler; + + /** + * Storage used by the driver for the uniform + */ + unsigned num_driver_storage; + struct gl_uniform_driver_storage *driver_storage; + + /** + * Storage used by Mesa for the uniform + * + * This form of the uniform is used by Mesa's implementation of \c + * glGetUniform. It can also be used by drivers to obtain the value of the + * uniform if the \c ::driver_storage interface is not used. + */ + union gl_constant_value *storage; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* IR_UNIFORM_H */ diff --git a/mesalib/src/glsl/link_uniforms.cpp b/mesalib/src/glsl/link_uniforms.cpp index 6dd1f5354..b9d5361b0 100644 --- a/mesalib/src/glsl/link_uniforms.cpp +++ b/mesalib/src/glsl/link_uniforms.cpp @@ -24,6 +24,7 @@ #include "main/core.h" #include "ir.h" #include "linker.h" +#include "ir_uniform.h" #include "glsl_symbol_table.h" #include "program/hash_table.h" @@ -34,6 +35,21 @@ * \author Ian Romanick <ian.d.romanick@intel.com> */ +/** + * Count the backing storage requirements for a type + */ +static unsigned +values_for_type(const glsl_type *type) +{ + if (type->is_sampler()) { + return 1; + } else if (type->is_array() && type->fields.array->is_sampler()) { + return type->array_size(); + } else { + return type->component_slots(); + } +} + void uniform_field_visitor::process(ir_variable *var) { @@ -83,3 +99,236 @@ uniform_field_visitor::recursion(const glsl_type *t, char **name, this->visit_field(t, *name); } } + +/** + * Class to help calculate the storage requirements for a set of uniforms + * + * As uniforms are added to the active set the number of active uniforms and + * the storage requirements for those uniforms are accumulated. The active + * uniforms are added the the hash table supplied to the constructor. + * + * If the same uniform is added multiple times (i.e., once for each shader + * target), it will only be accounted once. + */ +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) + { + /* empty */ + } + + /** + * Total number of active uniforms counted + */ + unsigned num_active_uniforms; + + /** + * Number of data values required to back the storage for the active uniforms + */ + unsigned num_values; + +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())); + + /* If the uniform is already in the map, there's nothing more to do. + */ + unsigned id; + if (this->map->get(id, name)) + return; + + char *key = strdup(name); + this->map->put(this->num_active_uniforms, key); + + /* Each leaf uniform occupies one entry in the list of active + * uniforms. + */ + this->num_active_uniforms++; + this->num_values += values_for_type(type); + } + + struct string_to_uint_map *map; +}; + +/** + * Class to help parcel out pieces of backing storage to uniforms + * + * Each uniform processed has some range of the \c gl_constant_value + * structures associated with it. The association is done by finding + * the uniform in the \c string_to_uint_map and using the value from + * the map to connect that slot in the \c gl_uniform_storage table + * with the next available slot in the \c gl_constant_value array. + * + * \warning + * This class assumes that every uniform that will be processed is + * already in the \c string_to_uint_map. In addition, it assumes that + * the \c gl_uniform_storage and \c gl_constant_value arrays are "big + * enough." + */ +class parcel_out_uniform_storage : public uniform_field_visitor { +public: + parcel_out_uniform_storage(struct string_to_uint_map *map, + struct gl_uniform_storage *uniforms, + union gl_constant_value *values) + : map(map), uniforms(uniforms), next_sampler(0), values(values) + { + /* empty */ + } + +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())); + + unsigned id; + bool found = this->map->get(id, name); + assert(found); + + if (!found) + return; + + /* If there is already storage associated with this uniform, it means + * that it was set while processing an earlier shader stage. For + * example, we may be processing the uniform in the fragment shader, but + * the uniform was already processed in the vertex shader. + */ + if (this->uniforms[id].storage != NULL) + return; + + const glsl_type *base_type; + if (type->is_array()) { + this->uniforms[id].array_elements = type->length; + base_type = type->fields.array; + } else { + this->uniforms[id].array_elements = 0; + base_type = type; + } + + if (base_type->is_sampler()) { + this->uniforms[id].sampler = this->next_sampler; + + /* Increment the sampler by 1 for non-arrays and by the number of + * array elements for arrays. + */ + this->next_sampler += MAX2(1, this->uniforms[id].array_elements); + } else { + this->uniforms[id].sampler = ~0; + } + + this->uniforms[id].name = strdup(name); + this->uniforms[id].type = base_type; + this->uniforms[id].initialized = 0; + this->uniforms[id].num_driver_storage = 0; + this->uniforms[id].driver_storage = NULL; + this->uniforms[id].storage = this->values; + + this->values += values_for_type(type); + } + + struct string_to_uint_map *map; + + struct gl_uniform_storage *uniforms; + unsigned next_sampler; + +public: + union gl_constant_value *values; +}; + +void +link_assign_uniform_locations(struct gl_shader_program *prog) +{ + ralloc_free(prog->UniformStorage); + prog->UniformStorage = NULL; + prog->NumUserUniformStorage = 0; + + if (prog->UniformHash != NULL) { + prog->UniformHash->clear(); + } else { + prog->UniformHash = new string_to_uint_map; + } + + for (unsigned i = 0; i < Elements(prog->SamplerUnits); i++) { + prog->SamplerUnits[i] = i; + } + + /* First pass: Count the uniform resources used by the user-defined + * uniforms. While this happens, each active uniform will have an index + * assigned to it. + * + * Note: this is *NOT* the index that is returned to the application by + * glGetUniformLocation. + */ + count_uniform_size uniform_size(prog->UniformHash); + 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)) + continue; + + /* FINISHME: Update code to process built-in uniforms! + */ + if (strncmp("gl_", var->name, 3) == 0) + continue; + + uniform_size.process(var); + } + } + + const unsigned num_user_uniforms = uniform_size.num_active_uniforms; + const unsigned num_data_slots = uniform_size.num_values; + + /* On the outside chance that there were no uniforms, bail out. + */ + if (num_user_uniforms == 0) + return; + + struct gl_uniform_storage *uniforms = + rzalloc_array(prog, struct gl_uniform_storage, num_user_uniforms); + union gl_constant_value *data = + rzalloc_array(uniforms, union gl_constant_value, num_data_slots); +#ifndef NDEBUG + union gl_constant_value *data_end = &data[num_data_slots]; +#endif + + parcel_out_uniform_storage parcel(prog->UniformHash, uniforms, data); + + 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)) + continue; + + /* FINISHME: Update code to process built-in uniforms! + */ + if (strncmp("gl_", var->name, 3) == 0) + continue; + + parcel.process(var); + } + } + +#ifndef NDEBUG + for (unsigned i = 0; i < num_user_uniforms; i++) { + assert(uniforms[i].storage != NULL); + } +#endif + + assert(parcel.values == data_end); + + prog->NumUserUniformStorage = num_user_uniforms; + prog->UniformStorage = uniforms; + + return; +} diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index 915d5bbcf..0306b7a1b 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -191,8 +191,8 @@ linker_warning(gl_shader_program *prog, const char *fmt, ...) void -invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, - int generic_base) +link_invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, + int generic_base) { foreach_list(node, sh->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); @@ -1021,13 +1021,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. @@ -1100,151 +1093,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. * @@ -1291,12 +1139,6 @@ find_available_slots(unsigned used_mask, unsigned needed_count) * \return * If locations are successfully assigned, true is returned. Otherwise an * error is emitted to the shader link log and false is returned. - * - * \bug - * Locations set via \c glBindFragDataLocation are not currently supported. - * Only locations assigned automatically by the linker, explicitly set by a - * layout qualifier, or explicitly set by a built-in variable (e.g., \c - * gl_FragColor) are supported for fragment shaders. */ bool assign_attribute_or_color_locations(gl_shader_program *prog, @@ -1320,7 +1162,8 @@ assign_attribute_or_color_locations(gl_shader_program *prog, * 1. Invalidate the location assignments for all vertex shader inputs. * * 2. Assign locations for inputs that have user-defined (via - * glBindVertexAttribLocation) locations. + * glBindVertexAttribLocation) locations and outputs that have + * user-defined locations (via glBindFragDataLocation). * * 3. Sort the attributes without assigned locations by number of slots * required in decreasing order. Fragmentation caused by attribute @@ -1337,7 +1180,7 @@ assign_attribute_or_color_locations(gl_shader_program *prog, (target_index == MESA_SHADER_VERTEX) ? ir_var_in : ir_var_out; - invalidate_variable_locations(sh, direction, generic_base); + link_invalidate_variable_locations(sh, direction, generic_base); /* Temporary storage for the set of attributes that need locations assigned. */ @@ -1381,6 +1224,13 @@ assign_attribute_or_color_locations(gl_shader_program *prog, 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; + } } /* If the variable is not a built-in and has a location statically @@ -1539,8 +1389,8 @@ assign_varying_locations(struct gl_context *ctx, * not being inputs. This lets the optimizer eliminate them. */ - invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); - invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0); + link_invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); + 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(); @@ -1858,7 +1708,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) } update_array_sizes(prog); - assign_uniform_locations(prog); + link_assign_uniform_locations(prog); /* 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 diff --git a/mesalib/src/glsl/linker.h b/mesalib/src/glsl/linker.h index 78c632961..433c63be2 100644 --- a/mesalib/src/glsl/linker.h +++ b/mesalib/src/glsl/linker.h @@ -30,6 +30,13 @@ extern bool link_function_calls(gl_shader_program *prog, gl_shader *main, gl_shader **shader_list, unsigned num_shaders); +extern void +link_invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, + int generic_base); + +extern void +link_assign_uniform_locations(struct gl_shader_program *prog); + /** * Class for processing all of the leaf fields of an uniform * |