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 <emil.velikov@collabora.com>
This commit is contained in:
Timothy Arceri
2015-11-14 15:13:28 +11:00
committed by Timothy Arceri
parent 0954b813a3
commit 02d2ab2378
5 changed files with 196 additions and 165 deletions
+16 -17
View File
@@ -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];
};
/*@}*/
+91 -37
View File
@@ -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) {
_mesa_glsl_error(loc, state, "%s layout qualifier is invalid (%d < 0)",
qual_indentifier, qual_value);
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;
}
*value = (unsigned) qual_value;
if (const_int->value.i[0] < 0) {
_mesa_glsl_error(loc, state, "%s layout qualifier is invalid (%d < 0)",
qual_indentifier, const_int->value.u[0]);
return false;
}
/* 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);
+24 -45
View File
@@ -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;
+27 -60
View File
@@ -298,7 +298,6 @@ static bool match_layout_qualifier(const char *s1, const char *s2,
%type <node> conditionopt
%type <node> for_init_statement
%type <for_rest_statement> for_rest_statement
%type <n> integer_constant
%type <node> 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) {
$$.invocations = new(ctx) ast_layout_expression(@1, $3);
if (!state->is_version(400, 0) &&
!state->ARB_gpu_shader5_enable) {
_mesa_glsl_error(& @3, state,
"invalid invocations %d specified", $3);
YYERROR;
} else if ($3 > MAX_GEOMETRY_SHADER_INVOCATIONS) {
_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");
}
}
+38 -6
View File
@@ -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: