From: Timothy Arceri Date: Sat, 14 Nov 2015 04:13:28 +0000 (+1100) Subject: glsl: add support for complie-time constant expressions X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=02d2ab23786a0f4ef635914801da97faf577197a;p=mesa.git glsl: add support for complie-time constant expressions This patch replaces the old interger constant qualifiers with either the new ast_layout_expression type if the qualifier requires merging or ast_expression if the qualifier can't have mulitple declarations or if all but the newest qualifier is simply ignored. We also update the process_qualifier_constant() helper to be similar to the one in the ast_layout_expression class, but in this case it will be used to process the ast_expression qualifiers. Global shader layout qualifier validation is moved out of the parser in this change as we now need to evaluate any constant expression before doing the validation. V2: Fix minimum value check for vertices (Emil) Reviewed-by: Emil Velikov --- diff --git a/src/glsl/ast.h b/src/glsl/ast.h index ca493401d7c..dfb036dde7e 100644 --- a/src/glsl/ast.h +++ b/src/glsl/ast.h @@ -578,7 +578,7 @@ struct ast_type_qualifier { unsigned precision:2; /** Geometry shader invocations for GL_ARB_gpu_shader5. */ - int invocations; + ast_layout_expression *invocations; /** * Location specified via GL_ARB_explicit_attrib_location layout @@ -586,20 +586,20 @@ struct ast_type_qualifier { * \note * This field is only valid if \c explicit_location is set. */ - int location; + ast_expression *location; /** * Index specified via GL_ARB_explicit_attrib_location layout * * \note * This field is only valid if \c explicit_index is set. */ - int index; + ast_expression *index; /** Maximum output vertices in GLSL 1.50 geometry shaders. */ - int max_vertices; + ast_layout_expression *max_vertices; /** Stream in GLSL 1.50 geometry shaders. */ - unsigned stream; + ast_expression *stream; /** * Input or output primitive type in GLSL 1.50 geometry shaders @@ -613,7 +613,7 @@ struct ast_type_qualifier { * \note * This field is only valid if \c explicit_binding is set. */ - int binding; + ast_expression *binding; /** * Offset specified via GL_ARB_shader_atomic_counter's "offset" @@ -622,14 +622,14 @@ struct ast_type_qualifier { * \note * This field is only valid if \c explicit_offset is set. */ - int offset; + ast_expression *offset; /** * Local size specified via GL_ARB_compute_shader's "local_size_{x,y,z}" * layout qualifier. Element i of this array is only valid if * flags.q.local_size & (1 << i) is set. */ - int local_size[3]; + ast_layout_expression *local_size[3]; /** Tessellation evaluation shader: vertex spacing (equal, fractional even/odd) */ GLenum vertex_spacing; @@ -641,7 +641,7 @@ struct ast_type_qualifier { bool point_mode; /** Tessellation control shader: number of output vertices */ - int vertices; + ast_layout_expression *vertices; /** * Image format specified with an ARB_shader_image_load_store @@ -1113,17 +1113,13 @@ public: class ast_tcs_output_layout : public ast_node { public: - ast_tcs_output_layout(const struct YYLTYPE &locp, int vertices) - : vertices(vertices) + ast_tcs_output_layout(const struct YYLTYPE &locp) { set_location(locp); } virtual ir_rvalue *hir(exec_list *instructions, struct _mesa_glsl_parse_state *state); - -private: - const int vertices; }; @@ -1155,9 +1151,12 @@ private: class ast_cs_input_layout : public ast_node { public: - ast_cs_input_layout(const struct YYLTYPE &locp, const unsigned *local_size) + ast_cs_input_layout(const struct YYLTYPE &locp, + ast_layout_expression **local_size) { - memcpy(this->local_size, local_size, sizeof(this->local_size)); + for (int i = 0; i < 3; i++) { + this->local_size[i] = local_size[i]; + } set_location(locp); } @@ -1165,7 +1164,7 @@ public: struct _mesa_glsl_parse_state *state); private: - unsigned local_size[3]; + ast_layout_expression *local_size[3]; }; /*@}*/ diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp index 5f70a184917..db8c450b587 100644 --- a/src/glsl/ast_to_hir.cpp +++ b/src/glsl/ast_to_hir.cpp @@ -2508,16 +2508,40 @@ static bool process_qualifier_constant(struct _mesa_glsl_parse_state *state, YYLTYPE *loc, const char *qual_indentifier, - int qual_value, + ast_expression *const_expression, unsigned *value) { - if (qual_value < 0) { + exec_list dummy_instructions; + + if (const_expression == NULL) { + *value = 0; + return true; + } + + ir_rvalue *const ir = const_expression->hir(&dummy_instructions, state); + + ir_constant *const const_int = ir->constant_expression_value(); + if (const_int == NULL || !const_int->type->is_integer()) { + _mesa_glsl_error(loc, state, "%s must be an integral constant " + "expression", qual_indentifier); + return false; + } + + if (const_int->value.i[0] < 0) { _mesa_glsl_error(loc, state, "%s layout qualifier is invalid (%d < 0)", - qual_indentifier, qual_value); + qual_indentifier, const_int->value.u[0]); return false; } - *value = (unsigned) qual_value; + /* If the location is const (and we've verified that + * it is) then no instructions should have been emitted + * when we converted it to HIR. If they were emitted, + * then either the location isn't const after all, or + * we are emitting unnecessary instructions. + */ + assert(dummy_instructions.is_empty()); + + *value = const_int->value.u[0]; return true; } @@ -3845,7 +3869,17 @@ handle_tess_ctrl_shader_output_decl(struct _mesa_glsl_parse_state *state, unsigned num_vertices = 0; if (state->tcs_output_vertices_specified) { - num_vertices = state->out_qualifier->vertices; + if (!state->out_qualifier->vertices-> + process_qualifier_constant(state, "vertices", + &num_vertices, false)) { + return; + } + + if (num_vertices > state->Const.MaxPatchVertices) { + _mesa_glsl_error(&loc, state, "vertices (%d) exceeds " + "GL_MAX_PATCH_VERTICES", num_vertices); + return; + } } if (!var->type->is_array() && !var->data.patch) { @@ -4079,9 +4113,18 @@ ast_declarator_list::hir(exec_list *instructions, */ if (decl_type && decl_type->contains_atomic()) { if (type->qualifier.flags.q.explicit_binding && - type->qualifier.flags.q.explicit_offset) - state->atomic_counter_offsets[type->qualifier.binding] = - type->qualifier.offset; + type->qualifier.flags.q.explicit_offset) { + unsigned qual_binding; + unsigned qual_offset; + if (process_qualifier_constant(state, &loc, "binding", + type->qualifier.binding, + &qual_binding) + && process_qualifier_constant(state, &loc, "offset", + type->qualifier.offset, + &qual_offset)) { + state->atomic_counter_offsets[qual_binding] = qual_offset; + } + } } if (this->declarations.is_empty()) { @@ -7055,22 +7098,18 @@ ast_tcs_output_layout::hir(exec_list *instructions, { YYLTYPE loc = this->get_location(); - /* If any tessellation control output layout declaration preceded this - * one, make sure it was consistent with this one. - */ - if (state->tcs_output_vertices_specified && - state->out_qualifier->vertices != this->vertices) { - _mesa_glsl_error(&loc, state, - "tessellation control shader output layout does not " - "match previous declaration"); - return NULL; + unsigned num_vertices; + if (!state->out_qualifier->vertices-> + process_qualifier_constant(state, "vertices", &num_vertices, + false)) { + /* return here to stop cascading incorrect error messages */ + return NULL; } /* If any shader outputs occurred before this declaration and specified an * array size, make sure the size they specified is consistent with the * primitive type. */ - unsigned num_vertices = this->vertices; if (state->tcs_output_size != 0 && state->tcs_output_size != num_vertices) { _mesa_glsl_error(&loc, state, "this tessellation control shader output layout " @@ -7178,20 +7217,6 @@ ast_cs_input_layout::hir(exec_list *instructions, { YYLTYPE loc = this->get_location(); - /* If any compute input layout declaration preceded this one, make sure it - * was consistent with this one. - */ - if (state->cs_input_local_size_specified) { - for (int i = 0; i < 3; i++) { - if (state->cs_input_local_size[i] != this->local_size[i]) { - _mesa_glsl_error(&loc, state, - "compute shader input layout does not match" - " previous declaration"); - return NULL; - } - } - } - /* From the ARB_compute_shader specification: * * If the local size of the shader in any dimension is greater @@ -7204,15 +7229,30 @@ ast_cs_input_layout::hir(exec_list *instructions, * report it at compile time as well. */ GLuint64 total_invocations = 1; + unsigned qual_local_size[3]; for (int i = 0; i < 3; i++) { - if (this->local_size[i] > state->ctx->Const.MaxComputeWorkGroupSize[i]) { + + char *local_size_str = ralloc_asprintf(NULL, "invalid local_size_%c", + 'x' + i); + /* Infer a local_size of 1 for unspecified dimensions */ + if (this->local_size[i] == NULL) { + qual_local_size[i] = 1; + } else if (!this->local_size[i]-> + process_qualifier_constant(state, local_size_str, + &qual_local_size[i], false)) { + ralloc_free(local_size_str); + return NULL; + } + ralloc_free(local_size_str); + + if (qual_local_size[i] > state->ctx->Const.MaxComputeWorkGroupSize[i]) { _mesa_glsl_error(&loc, state, "local_size_%c exceeds MAX_COMPUTE_WORK_GROUP_SIZE" " (%d)", 'x' + i, state->ctx->Const.MaxComputeWorkGroupSize[i]); break; } - total_invocations *= this->local_size[i]; + total_invocations *= qual_local_size[i]; if (total_invocations > state->ctx->Const.MaxComputeWorkGroupInvocations) { _mesa_glsl_error(&loc, state, @@ -7223,9 +7263,23 @@ ast_cs_input_layout::hir(exec_list *instructions, } } + /* If any compute input layout declaration preceded this one, make sure it + * was consistent with this one. + */ + if (state->cs_input_local_size_specified) { + for (int i = 0; i < 3; i++) { + if (state->cs_input_local_size[i] != qual_local_size[i]) { + _mesa_glsl_error(&loc, state, + "compute shader input layout does not match" + " previous declaration"); + return NULL; + } + } + } + state->cs_input_local_size_specified = true; for (int i = 0; i < 3; i++) - state->cs_input_local_size[i] = this->local_size[i]; + state->cs_input_local_size[i] = qual_local_size[i]; /* We may now declare the built-in constant gl_WorkGroupSize (see * builtin_variable_generator::generate_constants() for why we didn't @@ -7240,7 +7294,7 @@ ast_cs_input_layout::hir(exec_list *instructions, ir_constant_data data; memset(&data, 0, sizeof(data)); for (int i = 0; i < 3; i++) - data.u[i] = this->local_size[i]; + data.u[i] = qual_local_size[i]; var->constant_value = new(var) ir_constant(glsl_type::uvec3_type, &data); var->constant_initializer = new(var) ir_constant(glsl_type::uvec3_type, &data); diff --git a/src/glsl/ast_type.cpp b/src/glsl/ast_type.cpp index 89ab8eaa7ff..1e89a9e76b9 100644 --- a/src/glsl/ast_type.cpp +++ b/src/glsl/ast_type.cpp @@ -169,23 +169,19 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc, } if (q.flags.q.max_vertices) { - if (this->flags.q.max_vertices && this->max_vertices != q.max_vertices) { - _mesa_glsl_error(loc, state, - "geometry shader set conflicting max_vertices " - "(%d and %d)", this->max_vertices, q.max_vertices); - return false; + if (this->max_vertices) { + this->max_vertices->merge_qualifier(q.max_vertices); + } else { + this->max_vertices = q.max_vertices; } - this->max_vertices = q.max_vertices; } if (q.flags.q.invocations) { - if (this->flags.q.invocations && this->invocations != q.invocations) { - _mesa_glsl_error(loc, state, - "geometry shader set conflicting invocations " - "(%d and %d)", this->invocations, q.invocations); - return false; + if (this->invocations) { + this->invocations->merge_qualifier(q.invocations); + } else { + this->invocations = q.invocations; } - this->invocations = q.invocations; } if (state->stage == MESA_SHADER_GEOMETRY && @@ -208,14 +204,11 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc, } if (q.flags.q.vertices) { - if (this->flags.q.vertices && this->vertices != q.vertices) { - _mesa_glsl_error(loc, state, - "tessellation control shader set conflicting " - "vertices (%d and %d)", - this->vertices, q.vertices); - return false; + if (this->vertices) { + this->vertices->merge_qualifier(q.vertices); + } else { + this->vertices = q.vertices; } - this->vertices = q.vertices; } if (q.flags.q.vertex_spacing) { @@ -252,15 +245,11 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc, for (int i = 0; i < 3; i++) { if (q.flags.q.local_size & (1 << i)) { - if ((this->flags.q.local_size & (1 << i)) && - this->local_size[i] != q.local_size[i]) { - _mesa_glsl_error(loc, state, - "compute shader set conflicting values for " - "local_size_%c (%d and %d)", 'x' + i, - this->local_size[i], q.local_size[i]); - return false; + if (this->local_size[i]) { + this->local_size[i]->merge_qualifier(q.local_size[i]); + } else { + this->local_size[i] = q.local_size[i]; } - this->local_size[i] = q.local_size[i]; } } @@ -299,7 +288,7 @@ ast_type_qualifier::merge_out_qualifier(YYLTYPE *loc, const bool r = this->merge_qualifier(loc, state, q); if (state->stage == MESA_SHADER_TESS_CTRL) { - node = new(mem_ctx) ast_tcs_output_layout(*loc, q.vertices); + node = new(mem_ctx) ast_tcs_output_layout(*loc); } return r; @@ -403,15 +392,13 @@ ast_type_qualifier::merge_in_qualifier(YYLTYPE *loc, state->in_qualifier->prim_type = q.prim_type; } - if (this->flags.q.invocations && - q.flags.q.invocations && - this->invocations != q.invocations) { - _mesa_glsl_error(loc, state, - "conflicting invocations counts specified"); - return false; - } else if (q.flags.q.invocations) { + if (q.flags.q.invocations) { this->flags.q.invocations = 1; - this->invocations = q.invocations; + if (this->invocations) { + this->invocations->merge_qualifier(q.invocations); + } else { + this->invocations = q.invocations; + } } if (q.flags.q.early_fragment_tests) { @@ -454,15 +441,7 @@ ast_type_qualifier::merge_in_qualifier(YYLTYPE *loc, if (create_gs_ast) { node = new(mem_ctx) ast_gs_input_layout(*loc, q.prim_type); } else if (create_cs_ast) { - /* Infer a local_size of 1 for every unspecified dimension */ - unsigned local_size[3]; - for (int i = 0; i < 3; i++) { - if (q.flags.q.local_size & (1 << i)) - local_size[i] = q.local_size[i]; - else - local_size[i] = 1; - } - node = new(mem_ctx) ast_cs_input_layout(*loc, local_size); + node = new(mem_ctx) ast_cs_input_layout(*loc, q.local_size); } return true; diff --git a/src/glsl/glsl_parser.yy b/src/glsl/glsl_parser.yy index b4a1652a14c..5a8f98019d1 100644 --- a/src/glsl/glsl_parser.yy +++ b/src/glsl/glsl_parser.yy @@ -298,7 +298,6 @@ static bool match_layout_qualifier(const char *s1, const char *s2, %type conditionopt %type for_init_statement %type for_rest_statement -%type integer_constant %type layout_defaults %right THEN ELSE @@ -1152,11 +1151,6 @@ layout_qualifier_id_list: } ; -integer_constant: - INTCONSTANT { $$ = $1; } - | UINTCONSTANT { $$ = $1; } - ; - layout_qualifier_id: any_identifier { @@ -1453,9 +1447,18 @@ layout_qualifier_id: YYERROR; } } - | any_identifier '=' integer_constant + | any_identifier '=' constant_expression { memset(& $$, 0, sizeof($$)); + void *ctx = state; + + if ($3->oper != ast_int_constant && + $3->oper != ast_uint_constant && + !state->has_enhanced_layouts()) { + _mesa_glsl_error(& @1, state, + "compile-time constant expressions require " + "GLSL 4.40 or ARB_enhanced_layouts"); + } if (match_layout_qualifier("location", $1, state) == 0) { $$.flags.q.explicit_location = 1; @@ -1495,18 +1498,11 @@ layout_qualifier_id: if (match_layout_qualifier("max_vertices", $1, state) == 0) { $$.flags.q.max_vertices = 1; - - if ($3 < 0) { + $$.max_vertices = new(ctx) ast_layout_expression(@1, $3); + if (!state->is_version(150, 0)) { _mesa_glsl_error(& @3, state, - "invalid max_vertices %d specified", $3); - YYERROR; - } else { - $$.max_vertices = $3; - if (!state->is_version(150, 0)) { - _mesa_glsl_error(& @3, state, - "#version 150 max_vertices qualifier " - "specified", $3); - } + "#version 150 max_vertices qualifier " + "specified", $3); } } @@ -1527,12 +1523,7 @@ layout_qualifier_id: for (int i = 0; i < 3; i++) { if (match_layout_qualifier(local_size_qualifiers[i], $1, state) == 0) { - if ($3 <= 0) { - _mesa_glsl_error(& @3, state, - "invalid %s of %d specified", - local_size_qualifiers[i], $3); - YYERROR; - } else if (!state->has_compute_shader()) { + if (!state->has_compute_shader()) { _mesa_glsl_error(& @3, state, "%s qualifier requires GLSL 4.30 or " "GLSL ES 3.10 or ARB_compute_shader", @@ -1540,7 +1531,7 @@ layout_qualifier_id: YYERROR; } else { $$.flags.q.local_size |= (1 << i); - $$.local_size[i] = $3; + $$.local_size[i] = new(ctx) ast_layout_expression(@1, $3); } break; } @@ -1548,48 +1539,24 @@ layout_qualifier_id: if (match_layout_qualifier("invocations", $1, state) == 0) { $$.flags.q.invocations = 1; - - if ($3 <= 0) { - _mesa_glsl_error(& @3, state, - "invalid invocations %d specified", $3); - YYERROR; - } else if ($3 > MAX_GEOMETRY_SHADER_INVOCATIONS) { + $$.invocations = new(ctx) ast_layout_expression(@1, $3); + if (!state->is_version(400, 0) && + !state->ARB_gpu_shader5_enable) { _mesa_glsl_error(& @3, state, - "invocations (%d) exceeds " - "GL_MAX_GEOMETRY_SHADER_INVOCATIONS", $3); - YYERROR; - } else { - $$.invocations = $3; - if (!state->is_version(400, 0) && - !state->ARB_gpu_shader5_enable) { - _mesa_glsl_error(& @3, state, - "GL_ARB_gpu_shader5 invocations " - "qualifier specified", $3); - } + "GL_ARB_gpu_shader5 invocations " + "qualifier specified", $3); } } /* Layout qualifiers for tessellation control shaders. */ if (match_layout_qualifier("vertices", $1, state) == 0) { $$.flags.q.vertices = 1; - - if ($3 <= 0) { - _mesa_glsl_error(& @3, state, - "invalid vertices (%d) specified", $3); - YYERROR; - } else if ($3 > (int)state->Const.MaxPatchVertices) { - _mesa_glsl_error(& @3, state, - "vertices (%d) exceeds " - "GL_MAX_PATCH_VERTICES", $3); - YYERROR; - } else { - $$.vertices = $3; - if (!state->ARB_tessellation_shader_enable && - !state->is_version(400, 0)) { - _mesa_glsl_error(& @1, state, - "vertices qualifier requires GLSL 4.00 or " - "ARB_tessellation_shader"); - } + $$.vertices = new(ctx) ast_layout_expression(@1, $3); + if (!state->ARB_tessellation_shader_enable && + !state->is_version(400, 0)) { + _mesa_glsl_error(& @1, state, + "vertices qualifier requires GLSL 4.00 or " + "ARB_tessellation_shader"); } } diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp index 13a3c941e6a..84b3aca1a82 100644 --- a/src/glsl/glsl_parser_extras.cpp +++ b/src/glsl/glsl_parser_extras.cpp @@ -1648,8 +1648,20 @@ set_shader_inout_layout(struct gl_shader *shader, switch (shader->Stage) { case MESA_SHADER_TESS_CTRL: shader->TessCtrl.VerticesOut = 0; - if (state->tcs_output_vertices_specified) - shader->TessCtrl.VerticesOut = state->out_qualifier->vertices; + if (state->tcs_output_vertices_specified) { + unsigned vertices; + if (state->out_qualifier->vertices-> + process_qualifier_constant(state, "vertices", &vertices, + false)) { + + YYLTYPE loc = state->out_qualifier->vertices->get_location(); + if (vertices > state->Const.MaxPatchVertices) { + _mesa_glsl_error(&loc, state, "vertices (%d) exceeds " + "GL_MAX_PATCH_VERTICES", vertices); + } + shader->TessCtrl.VerticesOut = vertices; + } + } break; case MESA_SHADER_TESS_EVAL: shader->TessEval.PrimitiveMode = PRIM_UNKNOWN; @@ -1670,8 +1682,14 @@ set_shader_inout_layout(struct gl_shader *shader, break; case MESA_SHADER_GEOMETRY: shader->Geom.VerticesOut = 0; - if (state->out_qualifier->flags.q.max_vertices) - shader->Geom.VerticesOut = state->out_qualifier->max_vertices; + if (state->out_qualifier->flags.q.max_vertices) { + unsigned qual_max_vertices; + if (state->out_qualifier->max_vertices-> + process_qualifier_constant(state, "max_vertices", + &qual_max_vertices, true)) { + shader->Geom.VerticesOut = qual_max_vertices; + } + } if (state->gs_input_prim_type_specified) { shader->Geom.InputType = state->in_qualifier->prim_type; @@ -1686,8 +1704,22 @@ set_shader_inout_layout(struct gl_shader *shader, } shader->Geom.Invocations = 0; - if (state->in_qualifier->flags.q.invocations) - shader->Geom.Invocations = state->in_qualifier->invocations; + if (state->in_qualifier->flags.q.invocations) { + unsigned invocations; + if (state->in_qualifier->invocations-> + process_qualifier_constant(state, "invocations", + &invocations, false)) { + + YYLTYPE loc = state->in_qualifier->invocations->get_location(); + if (invocations > MAX_GEOMETRY_SHADER_INVOCATIONS) { + _mesa_glsl_error(&loc, state, + "invocations (%d) exceeds " + "GL_MAX_GEOMETRY_SHADER_INVOCATIONS", + invocations); + } + shader->Geom.Invocations = invocations; + } + } break; case MESA_SHADER_COMPUTE: