mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-21 15:50:11 +01:00
glsl: Implement rules for geometry shader input sizes.
Section 4.3.8.1 (Input Layout Qualifiers) of the GLSL 1.50 spec contains some tricky rules for how the sizes of geometry shader input arrays are related to the input layout specification. In essence, those rules boil down to the following: - If an input array declaration does not specify a size, and it follows an input layout declaration, it is sized according to the input layout. - If an input layout declaration follows an input array declaration that didn't specify a size, the input array declaration is given a size at the time the input layout declaration appears. - All input layout declarations and input array sizes must ultimately match. Inconsistencies are reported as soon as they are detected, at compile time if the inconsistency is within one compilation unit, otherwise at link time. - At least one compilation unit must contain an input layout declaration. (Note: the geom_array_resize_visitor class was contributed by Bryan Cain <bryancain3@gmail.com>.) Reviewed-by: Ian Romanick <ian.d.romanick@intel.com> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
This commit is contained in:
parent
20ae8e0c91
commit
7cfefe6965
3 changed files with 202 additions and 0 deletions
|
|
@ -2523,6 +2523,73 @@ process_initializer(ir_variable *var, ast_declaration *decl,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do additional processing necessary for geometry shader input array
|
||||||
|
* declarations (this covers both interface blocks arrays and input variable
|
||||||
|
* arrays).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_geometry_shader_input_decl(struct _mesa_glsl_parse_state *state,
|
||||||
|
YYLTYPE loc, ir_variable *var)
|
||||||
|
{
|
||||||
|
unsigned num_vertices = 0;
|
||||||
|
if (state->gs_input_prim_type_specified) {
|
||||||
|
num_vertices = vertices_per_prim(state->gs_input_prim_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(var->type->is_array());
|
||||||
|
if (var->type->length == 0) {
|
||||||
|
/* Section 4.3.8.1 (Input Layout Qualifiers) of the GLSL 1.50 spec says:
|
||||||
|
*
|
||||||
|
* All geometry shader input unsized array declarations will be
|
||||||
|
* sized by an earlier input layout qualifier, when present, as per
|
||||||
|
* the following table.
|
||||||
|
*
|
||||||
|
* Followed by a table mapping each allowed input layout qualifier to
|
||||||
|
* the corresponding input length.
|
||||||
|
*/
|
||||||
|
if (num_vertices != 0)
|
||||||
|
var->type = glsl_type::get_array_instance(var->type->fields.array,
|
||||||
|
num_vertices);
|
||||||
|
} else {
|
||||||
|
/* Section 4.3.8.1 (Input Layout Qualifiers) of the GLSL 1.50 spec
|
||||||
|
* includes the following examples of compile-time errors:
|
||||||
|
*
|
||||||
|
* // code sequence within one shader...
|
||||||
|
* in vec4 Color1[]; // size unknown
|
||||||
|
* ...Color1.length()...// illegal, length() unknown
|
||||||
|
* in vec4 Color2[2]; // size is 2
|
||||||
|
* ...Color1.length()...// illegal, Color1 still has no size
|
||||||
|
* in vec4 Color3[3]; // illegal, input sizes are inconsistent
|
||||||
|
* layout(lines) in; // legal, input size is 2, matching
|
||||||
|
* in vec4 Color4[3]; // illegal, contradicts layout
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* To detect the case illustrated by Color3, we verify that the size of
|
||||||
|
* an explicitly-sized array matches the size of any previously declared
|
||||||
|
* explicitly-sized array. To detect the case illustrated by Color4, we
|
||||||
|
* verify that the size of an explicitly-sized array is consistent with
|
||||||
|
* any previously declared input layout.
|
||||||
|
*/
|
||||||
|
if (num_vertices != 0 && var->type->length != num_vertices) {
|
||||||
|
_mesa_glsl_error(&loc, state,
|
||||||
|
"geometry shader input size contradicts previously"
|
||||||
|
" declared layout (size is %u, but layout requires a"
|
||||||
|
" size of %u)", var->type->length, num_vertices);
|
||||||
|
} else if (state->gs_input_size != 0 &&
|
||||||
|
var->type->length != state->gs_input_size) {
|
||||||
|
_mesa_glsl_error(&loc, state,
|
||||||
|
"geometry shader input sizes are "
|
||||||
|
"inconsistent (size is %u, but a previous "
|
||||||
|
"declaration has size %u)",
|
||||||
|
var->type->length, state->gs_input_size);
|
||||||
|
} else {
|
||||||
|
state->gs_input_size = var->type->length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ir_rvalue *
|
ir_rvalue *
|
||||||
ast_declarator_list::hir(exec_list *instructions,
|
ast_declarator_list::hir(exec_list *instructions,
|
||||||
struct _mesa_glsl_parse_state *state)
|
struct _mesa_glsl_parse_state *state)
|
||||||
|
|
@ -2833,6 +2900,8 @@ ast_declarator_list::hir(exec_list *instructions,
|
||||||
_mesa_glsl_error(&loc, state,
|
_mesa_glsl_error(&loc, state,
|
||||||
"geometry shader inputs must be arrays");
|
"geometry shader inputs must be arrays");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_geometry_shader_input_decl(state, loc, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4450,6 +4519,8 @@ ast_interface_block::hir(exec_list *instructions,
|
||||||
}
|
}
|
||||||
|
|
||||||
var->interface_type = block_type;
|
var->interface_type = block_type;
|
||||||
|
if (state->target == geometry_shader)
|
||||||
|
handle_geometry_shader_input_decl(state, loc, var);
|
||||||
state->symbols->add_variable(var);
|
state->symbols->add_variable(var);
|
||||||
instructions->push_tail(var);
|
instructions->push_tail(var);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4498,9 +4569,50 @@ ast_gs_input_layout::hir(exec_list *instructions,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If any shader inputs occurred before this declaration and specified an
|
||||||
|
* array size, make sure the size they specified is consistent with the
|
||||||
|
* primitive type.
|
||||||
|
*/
|
||||||
|
unsigned num_vertices = vertices_per_prim(this->prim_type);
|
||||||
|
if (state->gs_input_size != 0 && state->gs_input_size != num_vertices) {
|
||||||
|
_mesa_glsl_error(&loc, state,
|
||||||
|
"this geometry shader input layout implies %u vertices"
|
||||||
|
" per primitive, but a previous input is declared"
|
||||||
|
" with size %u", num_vertices, state->gs_input_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
state->gs_input_prim_type_specified = true;
|
state->gs_input_prim_type_specified = true;
|
||||||
state->gs_input_prim_type = this->prim_type;
|
state->gs_input_prim_type = this->prim_type;
|
||||||
|
|
||||||
|
/* If any shader inputs occurred before this declaration and did not
|
||||||
|
* specify an array size, their size is determined now.
|
||||||
|
*/
|
||||||
|
foreach_list (node, instructions) {
|
||||||
|
ir_variable *var = ((ir_instruction *) node)->as_variable();
|
||||||
|
if (var == NULL || var->mode != ir_var_shader_in)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Note: gl_PrimitiveIDIn has mode ir_var_shader_in, but it's not an
|
||||||
|
* array; skip it.
|
||||||
|
*/
|
||||||
|
if (!var->type->is_array())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (var->type->length == 0) {
|
||||||
|
if (var->max_array_access >= num_vertices) {
|
||||||
|
_mesa_glsl_error(&loc, state,
|
||||||
|
"this geometry shader input layout implies %u"
|
||||||
|
" vertices, but an access to element %u of input"
|
||||||
|
" `%s' already exists", num_vertices,
|
||||||
|
var->max_array_access, var->name);
|
||||||
|
} else {
|
||||||
|
var->type = glsl_type::get_array_instance(var->type->fields.array,
|
||||||
|
num_vertices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,15 @@ struct _mesa_glsl_parse_state {
|
||||||
/** Shaders containing built-in functions that are used for linking. */
|
/** Shaders containing built-in functions that are used for linking. */
|
||||||
struct gl_shader *builtins_to_link[16];
|
struct gl_shader *builtins_to_link[16];
|
||||||
unsigned num_builtins_to_link;
|
unsigned num_builtins_to_link;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For geometry shaders, size of the most recently seen input declaration
|
||||||
|
* that was a sized array, or 0 if no sized input array declarations have
|
||||||
|
* been seen.
|
||||||
|
*
|
||||||
|
* Unused for other shader types.
|
||||||
|
*/
|
||||||
|
unsigned gs_input_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
# define YYLLOC_DEFAULT(Current, Rhs, N) \
|
# define YYLLOC_DEFAULT(Current, Rhs, N) \
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,77 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class geom_array_resize_visitor : public ir_hierarchical_visitor {
|
||||||
|
public:
|
||||||
|
unsigned num_vertices;
|
||||||
|
gl_shader_program *prog;
|
||||||
|
|
||||||
|
geom_array_resize_visitor(unsigned num_vertices, gl_shader_program *prog)
|
||||||
|
{
|
||||||
|
this->num_vertices = num_vertices;
|
||||||
|
this->prog = prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~geom_array_resize_visitor()
|
||||||
|
{
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ir_visitor_status visit(ir_variable *var)
|
||||||
|
{
|
||||||
|
if (!var->type->is_array() || var->mode != ir_var_shader_in)
|
||||||
|
return visit_continue;
|
||||||
|
|
||||||
|
unsigned size = var->type->length;
|
||||||
|
|
||||||
|
/* Generate a link error if the shader has declared this array with an
|
||||||
|
* incorrect size.
|
||||||
|
*/
|
||||||
|
if (size && size != this->num_vertices) {
|
||||||
|
linker_error(this->prog, "size of array %s declared as %u, "
|
||||||
|
"but number of input vertices is %u\n",
|
||||||
|
var->name, size, this->num_vertices);
|
||||||
|
return visit_continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate a link error if the shader attempts to access an input
|
||||||
|
* array using an index too large for its actual size assigned at link
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
if (var->max_array_access >= this->num_vertices) {
|
||||||
|
linker_error(this->prog, "geometry shader accesses element %i of "
|
||||||
|
"%s, but only %i input vertices\n",
|
||||||
|
var->max_array_access, var->name, this->num_vertices);
|
||||||
|
return visit_continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var->type = glsl_type::get_array_instance(var->type->element_type(),
|
||||||
|
this->num_vertices);
|
||||||
|
var->max_array_access = this->num_vertices - 1;
|
||||||
|
|
||||||
|
return visit_continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dereferences of input variables need to be updated so that their type
|
||||||
|
* matches the newly assigned type of the variable they are accessing. */
|
||||||
|
virtual ir_visitor_status visit(ir_dereference_variable *ir)
|
||||||
|
{
|
||||||
|
ir->type = ir->var->type;
|
||||||
|
return visit_continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dereferences of 2D input arrays need to be updated so that their type
|
||||||
|
* matches the newly assigned type of the array they are accessing. */
|
||||||
|
virtual ir_visitor_status visit_leave(ir_dereference_array *ir)
|
||||||
|
{
|
||||||
|
const glsl_type *const vt = ir->array->type;
|
||||||
|
if (vt->is_array())
|
||||||
|
ir->type = vt->element_type();
|
||||||
|
return visit_continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
linker_error(gl_shader_program *prog, const char *fmt, ...)
|
linker_error(gl_shader_program *prog, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
|
@ -1173,6 +1244,16 @@ link_intrastage_shaders(void *mem_ctx,
|
||||||
if (linked)
|
if (linked)
|
||||||
validate_ir_tree(linked->ir);
|
validate_ir_tree(linked->ir);
|
||||||
|
|
||||||
|
/* Set the size of geometry shader input arrays */
|
||||||
|
if (linked->Type == GL_GEOMETRY_SHADER) {
|
||||||
|
unsigned num_vertices = vertices_per_prim(prog->Geom.InputType);
|
||||||
|
geom_array_resize_visitor input_resize_visitor(num_vertices, prog);
|
||||||
|
foreach_iter(exec_list_iterator, iter, *linked->ir) {
|
||||||
|
ir_instruction *ir = (ir_instruction *)iter.get();
|
||||||
|
ir->accept(&input_resize_visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Make a pass over all variable declarations to ensure that arrays with
|
/* Make a pass over all variable declarations to ensure that arrays with
|
||||||
* unspecified sizes have a size specified. The size is inferred from the
|
* unspecified sizes have a size specified. The size is inferred from the
|
||||||
* max_array_access field.
|
* max_array_access field.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue