From 0fa74e848fd11c71bb70ea0a69650944e4f07571 Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Mon, 6 Jan 2014 09:09:31 -0800 Subject: [PATCH] glsl/cs: Handle compute shader local_size_{x,y,z} declaration. Reviewed-by: Jordan Justen --- src/glsl/ast.h | 34 ++++++++++++ src/glsl/ast_to_hir.cpp | 79 +++++++++++++++++++++++++++ src/glsl/ast_type.cpp | 14 +++++ src/glsl/builtin_variables.cpp | 20 +++++++ src/glsl/glsl_lexer.ll | 3 +- src/glsl/glsl_parser.yy | 94 +++++++++++++++++++++++++-------- src/glsl/glsl_parser_extras.cpp | 51 +++++++++++++----- src/glsl/glsl_parser_extras.h | 15 ++++++ src/mesa/main/mtypes.h | 11 ++++ 9 files changed, 285 insertions(+), 36 deletions(-) diff --git a/src/glsl/ast.h b/src/glsl/ast.h index 2d6f3a293a0..61fd923bcf9 100644 --- a/src/glsl/ast.h +++ b/src/glsl/ast.h @@ -460,6 +460,12 @@ struct ast_type_qualifier { unsigned prim_type:1; unsigned max_vertices:1; /** \} */ + + /** + * local_size_{x,y,z} flags for compute shaders. Bit 0 represents + * local_size_x, and so on. + */ + unsigned local_size:3; } /** \brief Set of flags, accessed by name. */ q; @@ -509,6 +515,13 @@ struct ast_type_qualifier { */ int 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]; + /** * Return true if and only if an interpolation qualifier is present. */ @@ -989,6 +1002,27 @@ private: const GLenum prim_type; }; + +/** + * AST node representing a decalaration of the input layout for compute + * shaders. + */ +class ast_cs_input_layout : public ast_node +{ +public: + ast_cs_input_layout(const struct YYLTYPE &locp, const unsigned *local_size) + { + memcpy(this->local_size, local_size, sizeof(this->local_size)); + set_location(locp); + } + + virtual ir_rvalue *hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + +private: + unsigned local_size[3]; +}; + /*@}*/ extern void diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp index fc28703ddc3..a55c9694238 100644 --- a/src/glsl/ast_to_hir.cpp +++ b/src/glsl/ast_to_hir.cpp @@ -77,6 +77,7 @@ _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) state->toplevel_ir = instructions; state->gs_input_prim_type_specified = false; + state->cs_input_local_size_specified = false; /* Section 4.2 of the GLSL 1.20 specification states: * "The built-in functions are scoped in a scope outside the global scope @@ -5303,6 +5304,84 @@ ast_gs_input_layout::hir(exec_list *instructions, } +ir_rvalue * +ast_cs_input_layout::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + 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 + * than the maximum size supported by the implementation for that + * dimension, a compile-time error results. + * + * It is not clear from the spec how the error should be reported if + * the total size of the work group exceeds + * MAX_COMPUTE_WORK_GROUP_INVOCATIONS, but it seems reasonable to + * report it at compile time as well. + */ + GLuint64 total_invocations = 1; + for (int i = 0; i < 3; i++) { + if (this->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]; + if (total_invocations > + state->ctx->Const.MaxComputeWorkGroupInvocations) { + _mesa_glsl_error(&loc, state, + "product of local_sizes exceeds " + "MAX_COMPUTE_WORK_GROUP_INVOCATIONS (%d)", + state->ctx->Const.MaxComputeWorkGroupInvocations); + break; + } + } + + state->cs_input_local_size_specified = true; + for (int i = 0; i < 3; i++) + state->cs_input_local_size[i] = this->local_size[i]; + + /* We may now declare the built-in constant gl_WorkGroupSize (see + * builtin_variable_generator::generate_constants() for why we didn't + * declare it earlier). + */ + ir_variable *var = new(state->symbols) + ir_variable(glsl_type::ivec3_type, "gl_WorkGroupSize", ir_var_auto); + var->data.how_declared = ir_var_declared_implicitly; + var->data.read_only = true; + instructions->push_tail(var); + state->symbols->add_variable(var); + ir_constant_data data; + memset(&data, 0, sizeof(data)); + for (int i = 0; i < 3; i++) + data.i[i] = this->local_size[i]; + var->constant_value = new(var) ir_constant(glsl_type::ivec3_type, &data); + var->constant_initializer = + new(var) ir_constant(glsl_type::ivec3_type, &data); + var->data.has_initializer = true; + + return NULL; +} + + static void detect_conflicting_assignments(struct _mesa_glsl_parse_state *state, exec_list *instructions) diff --git a/src/glsl/ast_type.cpp b/src/glsl/ast_type.cpp index 637da0dfb7c..fe11508a0a4 100644 --- a/src/glsl/ast_type.cpp +++ b/src/glsl/ast_type.cpp @@ -158,6 +158,20 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc, if ((q.flags.i & ubo_layout_mask.flags.i) != 0) this->flags.i &= ~ubo_layout_mask.flags.i; + 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; + } + this->local_size[i] = q.local_size[i]; + } + } + this->flags.i |= q.flags.i; if (q.flags.q.explicit_location) diff --git a/src/glsl/builtin_variables.cpp b/src/glsl/builtin_variables.cpp index 3e7a84521e9..cc423383df8 100644 --- a/src/glsl/builtin_variables.cpp +++ b/src/glsl/builtin_variables.cpp @@ -690,6 +690,26 @@ builtin_variable_generator::generate_constants() state->Const.MaxComputeWorkGroupSize[0], state->Const.MaxComputeWorkGroupSize[1], state->Const.MaxComputeWorkGroupSize[2]); + + /* From the GLSL 4.40 spec, section 7.1 (Built-In Language Variables): + * + * The built-in constant gl_WorkGroupSize is a compute-shader + * constant containing the local work-group size of the shader. The + * size of the work group in the X, Y, and Z dimensions is stored in + * the x, y, and z components. The constants values in + * gl_WorkGroupSize will match those specified in the required + * local_size_x, local_size_y, and local_size_z layout qualifiers + * for the current shader. This is a constant so that it can be + * used to size arrays of memory that can be shared within the local + * work group. It is a compile-time error to use gl_WorkGroupSize + * in a shader that does not declare a fixed local group size, or + * before that shader has declared a fixed local group size, using + * local_size_x, local_size_y, and local_size_z. + * + * To prevent the shader from trying to refer to gl_WorkGroupSize before + * the layout declaration, we don't define it here. Intead we define it + * in ast_cs_input_layout::hir(). + */ } } diff --git a/src/glsl/glsl_lexer.ll b/src/glsl/glsl_lexer.ll index 50875bf3b23..3208b32dae5 100644 --- a/src/glsl/glsl_lexer.ll +++ b/src/glsl/glsl_lexer.ll @@ -349,7 +349,8 @@ layout { || yyextra->ARB_explicit_attrib_location_enable || yyextra->ARB_uniform_buffer_object_enable || yyextra->ARB_fragment_coord_conventions_enable - || yyextra->ARB_shading_language_420pack_enable) { + || yyextra->ARB_shading_language_420pack_enable + || yyextra->ARB_compute_shader_enable) { return LAYOUT_TOK; } else { yylval->identifier = strdup(yytext); diff --git a/src/glsl/glsl_parser.yy b/src/glsl/glsl_parser.yy index 928c57e20d1..b26c2030fe1 100644 --- a/src/glsl/glsl_parser.yy +++ b/src/glsl/glsl_parser.yy @@ -1291,6 +1291,34 @@ layout_qualifier_id: } } + static const char *local_size_qualifiers[3] = { + "local_size_x", + "local_size_y", + "local_size_z", + }; + 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->is_version(430, 0) && + !state->ARB_compute_shader_enable) { + _mesa_glsl_error(& @3, state, + "%s qualifier requires GLSL 4.30 or " + "ARB_compute_shader", + local_size_qualifiers[i]); + YYERROR; + } else { + $$.flags.q.local_size |= (1 << i); + $$.local_size[i] = $3; + } + break; + } + } + /* If the identifier didn't match any known layout identifiers, * emit an error. */ @@ -2334,29 +2362,53 @@ layout_defaults: { void *ctx = state; $$ = NULL; - if (state->stage != MESA_SHADER_GEOMETRY) { + switch (state->stage) { + case MESA_SHADER_GEOMETRY: { + if (!$1.flags.q.prim_type) { + _mesa_glsl_error(& @1, state, + "input layout qualifiers must specify a primitive" + " type"); + } else { + /* Make sure this is a valid input primitive type. */ + switch ($1.prim_type) { + case GL_POINTS: + case GL_LINES: + case GL_LINES_ADJACENCY: + case GL_TRIANGLES: + case GL_TRIANGLES_ADJACENCY: + $$ = new(ctx) ast_gs_input_layout(@1, $1.prim_type); + break; + default: + _mesa_glsl_error(&@1, state, + "invalid geometry shader input primitive type"); + break; + } + } + } + break; + case MESA_SHADER_COMPUTE: { + if ($1.flags.q.local_size == 0) { + _mesa_glsl_error(& @1, state, + "input layout qualifiers must specify a local " + "size"); + } else { + /* Infer a local_size of 1 for every unspecified dimension */ + unsigned local_size[3]; + for (int i = 0; i < 3; i++) { + if ($1.flags.q.local_size & (1 << i)) + local_size[i] = $1.local_size[i]; + else + local_size[i] = 1; + } + $$ = new(ctx) ast_cs_input_layout(@1, local_size); + } + } + break; + default: _mesa_glsl_error(& @1, state, "input layout qualifiers only valid in " - "geometry shaders"); - } else if (!$1.flags.q.prim_type) { - _mesa_glsl_error(& @1, state, - "input layout qualifiers must specify a primitive" - " type"); - } else { - /* Make sure this is a valid input primitive type. */ - switch ($1.prim_type) { - case GL_POINTS: - case GL_LINES: - case GL_LINES_ADJACENCY: - case GL_TRIANGLES: - case GL_TRIANGLES_ADJACENCY: - $$ = new(ctx) ast_gs_input_layout(@1, $1.prim_type); - break; - default: - _mesa_glsl_error(&@1, state, - "invalid geometry shader input primitive type"); - break; - } + "geometry and compute shaders"); + break; } } diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp index d45e63f9d2f..b822d229287 100644 --- a/src/glsl/glsl_parser_extras.cpp +++ b/src/glsl/glsl_parser_extras.cpp @@ -56,7 +56,8 @@ static unsigned known_desktop_glsl_versions[] = _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *_ctx, gl_shader_stage stage, void *mem_ctx) - : ctx(_ctx), switch_state() + : ctx(_ctx), cs_input_local_size_specified(false), cs_input_local_size(), + switch_state() { assert(stage < MESA_SHADER_STAGES); this->stage = stage; @@ -1339,23 +1340,45 @@ set_shader_inout_layout(struct gl_shader *shader, /* Should have been prevented by the parser. */ assert(!state->gs_input_prim_type_specified); assert(!state->out_qualifier->flags.i); - return; } - shader->Geom.VerticesOut = 0; - if (state->out_qualifier->flags.q.max_vertices) - shader->Geom.VerticesOut = state->out_qualifier->max_vertices; - - if (state->gs_input_prim_type_specified) { - shader->Geom.InputType = state->gs_input_prim_type; - } else { - shader->Geom.InputType = PRIM_UNKNOWN; + if (shader->Stage != MESA_SHADER_COMPUTE) { + /* Should have been prevented by the parser. */ + assert(!state->cs_input_local_size_specified); } - if (state->out_qualifier->flags.q.prim_type) { - shader->Geom.OutputType = state->out_qualifier->prim_type; - } else { - shader->Geom.OutputType = PRIM_UNKNOWN; + switch (shader->Stage) { + 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->gs_input_prim_type_specified) { + shader->Geom.InputType = state->gs_input_prim_type; + } else { + shader->Geom.InputType = PRIM_UNKNOWN; + } + + if (state->out_qualifier->flags.q.prim_type) { + shader->Geom.OutputType = state->out_qualifier->prim_type; + } else { + shader->Geom.OutputType = PRIM_UNKNOWN; + } + break; + + case MESA_SHADER_COMPUTE: + if (state->cs_input_local_size_specified) { + for (int i = 0; i < 3; i++) + shader->Comp.LocalSize[i] = state->cs_input_local_size[i]; + } else { + for (int i = 0; i < 3; i++) + shader->Comp.LocalSize[i] = 0; + } + break; + + default: + /* Nothing to do. */ + break; } } diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h index 20ed2cfddc6..7d661473d04 100644 --- a/src/glsl/glsl_parser_extras.h +++ b/src/glsl/glsl_parser_extras.h @@ -196,6 +196,21 @@ struct _mesa_glsl_parse_state { */ GLenum gs_input_prim_type; + /** + * True if a compute shader input local size was specified using a layout + * directive. + * + * Note: this value is computed at ast_to_hir time rather than at parse + * time. + */ + bool cs_input_local_size_specified; + + /** + * If cs_input_local_size_specified is true, the local size that was + * specified. Otherwise ignored. + */ + unsigned cs_input_local_size[3]; + /** Output layout qualifiers from GLSL 1.50. (geometry shader controls)*/ struct ast_type_qualifier *out_qualifier; diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index bd22d5003fd..36d98eeef87 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -2451,6 +2451,17 @@ struct gl_shader * ImageAccess arrays above. */ GLuint NumImages; + + /** + * Compute shader state from ARB_compute_shader layout qualifiers. + */ + struct { + /** + * Size specified using local_size_{x,y,z}, or all 0's to indicate that + * it's not set in this shader. + */ + unsigned LocalSize[3]; + } Comp; }; -- 2.30.2