glsl/cs: Handle compute shader local_size_{x,y,z} declaration.

Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
This commit is contained in:
Paul Berry
2014-01-06 09:09:31 -08:00
parent 0398b69954
commit 0fa74e848f
9 changed files with 285 additions and 36 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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().
*/
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;
};