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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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().
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user