glsl: allow linking of tessellation shaders.

Marek: require a tess eval shader if a tess control shader is present

Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Chris Forbes 2014-09-21 13:33:14 +12:00 committed by Marek Olšák
parent 1009b3311f
commit 7c758c5a21
3 changed files with 294 additions and 7 deletions

View File

@ -134,9 +134,9 @@ intrastage_match(interface_block_definition *a,
* Check if two interfaces match, according to interstage (in/out) interface
* matching rules.
*
* If \c extra_array_level is true, then vertex-to-geometry shader matching
* rules are enforced (i.e. a successful match requires the consumer interface
* to be an array and the producer interface to be a non-array).
* If \c extra_array_level is true, the consumer interface is required to be
* an array and the producer interface is required to be a non-array.
* This is used for tessellation control and geometry shader consumers.
*/
bool
interstage_match(const interface_block_definition *producer,
@ -318,7 +318,10 @@ validate_interstage_inout_blocks(struct gl_shader_program *prog,
const gl_shader *consumer)
{
interface_block_definitions definitions;
const bool extra_array_level = consumer->Stage == MESA_SHADER_GEOMETRY;
/* VS -> GS, VS -> TCS, VS -> TES, TES -> GS */
const bool extra_array_level = (producer->Stage == MESA_SHADER_VERTEX &&
consumer->Stage != MESA_SHADER_FRAGMENT) ||
consumer->Stage == MESA_SHADER_GEOMETRY;
/* Add input interfaces from the consumer to the symbol table. */
foreach_in_list(ir_instruction, node, consumer->ir) {

View File

@ -54,10 +54,16 @@ cross_validate_types_and_qualifiers(struct gl_shader_program *prog,
/* Check that the types match between stages.
*/
const glsl_type *type_to_match = input->type;
if (consumer_stage == MESA_SHADER_GEOMETRY) {
assert(type_to_match->is_array()); /* Enforced by ast_to_hir */
/* VS -> GS, VS -> TCS, VS -> TES, TES -> GS */
const bool extra_array_level = (producer_stage == MESA_SHADER_VERTEX &&
consumer_stage != MESA_SHADER_FRAGMENT) ||
consumer_stage == MESA_SHADER_GEOMETRY;
if (extra_array_level) {
assert(type_to_match->is_array());
type_to_match = type_to_match->fields.array;
}
if (type_to_match != output->type) {
/* There is a bit of a special case for gl_TexCoord. This
* built-in is unsized by default. Applications that variable

View File

@ -250,6 +250,53 @@ public:
}
};
class tess_eval_array_resize_visitor : public ir_hierarchical_visitor {
public:
unsigned num_vertices;
gl_shader_program *prog;
tess_eval_array_resize_visitor(unsigned num_vertices, gl_shader_program *prog)
{
this->num_vertices = num_vertices;
this->prog = prog;
}
virtual ~tess_eval_array_resize_visitor()
{
/* empty */
}
virtual ir_visitor_status visit(ir_variable *var)
{
if (!var->type->is_array() || var->data.mode != ir_var_shader_in || var->data.patch)
return visit_continue;
var->type = glsl_type::get_array_instance(var->type->fields.array,
this->num_vertices);
var->data.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->fields.array;
return visit_continue;
}
};
/**
* Visitor that determines the highest stream id to which a (geometry) shader
* emits vertices. It also checks whether End{Stream}Primitive is ever called.
@ -1401,6 +1448,167 @@ private:
hash_table *unnamed_interfaces;
};
/**
* Performs the cross-validation of tessellation control shader vertices and
* layout qualifiers for the attached tessellation control shaders,
* and propagates them to the linked TCS and linked shader program.
*/
static void
link_tcs_out_layout_qualifiers(struct gl_shader_program *prog,
struct gl_shader *linked_shader,
struct gl_shader **shader_list,
unsigned num_shaders)
{
linked_shader->TessCtrl.VerticesOut = 0;
if (linked_shader->Stage != MESA_SHADER_TESS_CTRL)
return;
/* From the GLSL 4.0 spec (chapter 4.3.8.2):
*
* "All tessellation control shader layout declarations in a program
* must specify the same output patch vertex count. There must be at
* least one layout qualifier specifying an output patch vertex count
* in any program containing tessellation control shaders; however,
* such a declaration is not required in all tessellation control
* shaders."
*/
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if (shader->TessCtrl.VerticesOut != 0) {
if (linked_shader->TessCtrl.VerticesOut != 0 &&
linked_shader->TessCtrl.VerticesOut != shader->TessCtrl.VerticesOut) {
linker_error(prog, "tessellation control shader defined with "
"conflicting output vertex count (%d and %d)\n",
linked_shader->TessCtrl.VerticesOut,
shader->TessCtrl.VerticesOut);
return;
}
linked_shader->TessCtrl.VerticesOut = shader->TessCtrl.VerticesOut;
}
}
/* Just do the intrastage -> interstage propagation right now,
* since we already know we're in the right type of shader program
* for doing it.
*/
if (linked_shader->TessCtrl.VerticesOut == 0) {
linker_error(prog, "tessellation control shader didn't declare "
"vertices out layout qualifier\n");
return;
}
prog->TessCtrl.VerticesOut = linked_shader->TessCtrl.VerticesOut;
}
/**
* Performs the cross-validation of tessellation evaluation shader
* primitive type, vertex spacing, ordering and point_mode layout qualifiers
* for the attached tessellation evaluation shaders, and propagates them
* to the linked TES and linked shader program.
*/
static void
link_tes_in_layout_qualifiers(struct gl_shader_program *prog,
struct gl_shader *linked_shader,
struct gl_shader **shader_list,
unsigned num_shaders)
{
linked_shader->TessEval.PrimitiveMode = PRIM_UNKNOWN;
linked_shader->TessEval.Spacing = 0;
linked_shader->TessEval.VertexOrder = 0;
linked_shader->TessEval.PointMode = -1;
if (linked_shader->Stage != MESA_SHADER_TESS_EVAL)
return;
/* From the GLSL 4.0 spec (chapter 4.3.8.1):
*
* "At least one tessellation evaluation shader (compilation unit) in
* a program must declare a primitive mode in its input layout.
* Declaration vertex spacing, ordering, and point mode identifiers is
* optional. It is not required that all tessellation evaluation
* shaders in a program declare a primitive mode. If spacing or
* vertex ordering declarations are omitted, the tessellation
* primitive generator will use equal spacing or counter-clockwise
* vertex ordering, respectively. If a point mode declaration is
* omitted, the tessellation primitive generator will produce lines or
* triangles according to the primitive mode."
*/
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
if (shader->TessEval.PrimitiveMode != PRIM_UNKNOWN) {
if (linked_shader->TessEval.PrimitiveMode != PRIM_UNKNOWN &&
linked_shader->TessEval.PrimitiveMode != shader->TessEval.PrimitiveMode) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting input primitive modes.\n");
return;
}
linked_shader->TessEval.PrimitiveMode = shader->TessEval.PrimitiveMode;
}
if (shader->TessEval.Spacing != 0) {
if (linked_shader->TessEval.Spacing != 0 &&
linked_shader->TessEval.Spacing != shader->TessEval.Spacing) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting vertex spacing.\n");
return;
}
linked_shader->TessEval.Spacing = shader->TessEval.Spacing;
}
if (shader->TessEval.VertexOrder != 0) {
if (linked_shader->TessEval.VertexOrder != 0 &&
linked_shader->TessEval.VertexOrder != shader->TessEval.VertexOrder) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting ordering.\n");
return;
}
linked_shader->TessEval.VertexOrder = shader->TessEval.VertexOrder;
}
if (shader->TessEval.PointMode != -1) {
if (linked_shader->TessEval.PointMode != -1 &&
linked_shader->TessEval.PointMode != shader->TessEval.PointMode) {
linker_error(prog, "tessellation evaluation shader defined with "
"conflicting point modes.\n");
return;
}
linked_shader->TessEval.PointMode = shader->TessEval.PointMode;
}
}
/* Just do the intrastage -> interstage propagation right now,
* since we already know we're in the right type of shader program
* for doing it.
*/
if (linked_shader->TessEval.PrimitiveMode == PRIM_UNKNOWN) {
linker_error(prog,
"tessellation evaluation shader didn't declare input "
"primitive modes.\n");
return;
}
prog->TessEval.PrimitiveMode = linked_shader->TessEval.PrimitiveMode;
if (linked_shader->TessEval.Spacing == 0)
linked_shader->TessEval.Spacing = GL_EQUAL;
prog->TessEval.Spacing = linked_shader->TessEval.Spacing;
if (linked_shader->TessEval.VertexOrder == 0)
linked_shader->TessEval.VertexOrder = GL_CCW;
prog->TessEval.VertexOrder = linked_shader->TessEval.VertexOrder;
if (linked_shader->TessEval.PointMode == -1)
linked_shader->TessEval.PointMode = GL_FALSE;
prog->TessEval.PointMode = linked_shader->TessEval.PointMode;
}
/**
* Performs the cross-validation of layout qualifiers specified in
* redeclaration of gl_FragCoord for the attached fragment shaders,
@ -1747,6 +1955,8 @@ link_intrastage_shaders(void *mem_ctx,
ralloc_steal(linked, linked->UniformBlocks);
link_fs_input_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_tcs_out_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_tes_in_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_gs_inout_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_cs_input_layout_qualifiers(prog, linked, shader_list, num_shaders);
@ -1922,6 +2132,34 @@ update_array_sizes(struct gl_shader_program *prog)
}
}
/**
* Resize tessellation evaluation per-vertex inputs to the size of
* tessellation control per-vertex outputs.
*/
static void
resize_tes_inputs(struct gl_context *ctx,
struct gl_shader_program *prog)
{
if (prog->_LinkedShaders[MESA_SHADER_TESS_EVAL] == NULL)
return;
gl_shader *const tcs = prog->_LinkedShaders[MESA_SHADER_TESS_CTRL];
gl_shader *const tes = prog->_LinkedShaders[MESA_SHADER_TESS_EVAL];
/* If no control shader is present, then the TES inputs are statically
* sized to MaxPatchVertices; the actual size of the arrays won't be
* known until draw time.
*/
const int num_vertices = tcs
? tcs->TessCtrl.VerticesOut
: ctx->Const.MaxPatchVertices;
tess_eval_array_resize_visitor input_resize_visitor(num_vertices, prog);
foreach_in_list(ir_instruction, ir, tes->ir) {
ir->accept(&input_resize_visitor);
}
}
/**
* Find a contiguous set of available bits in a bitmask.
*
@ -2897,7 +3135,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
prog->Version = max_version;
prog->IsES = is_es_prog;
/* Geometry shaders have to be linked with vertex shaders.
/* Some shaders have to be linked with some other shaders present.
*/
if (num_shaders[MESA_SHADER_GEOMETRY] > 0 &&
num_shaders[MESA_SHADER_VERTEX] == 0 &&
@ -2906,6 +3144,44 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
"vertex shader\n");
goto done;
}
if (num_shaders[MESA_SHADER_TESS_EVAL] > 0 &&
num_shaders[MESA_SHADER_VERTEX] == 0 &&
!prog->SeparateShader) {
linker_error(prog, "Tessellation evaluation shader must be linked with "
"vertex shader\n");
goto done;
}
if (num_shaders[MESA_SHADER_TESS_CTRL] > 0 &&
num_shaders[MESA_SHADER_VERTEX] == 0 &&
!prog->SeparateShader) {
linker_error(prog, "Tessellation control shader must be linked with "
"vertex shader\n");
goto done;
}
/* The spec is self-contradictory here. It allows linking without a tess
* eval shader, but that can only be used with transform feedback and
* rasterization disabled. However, transform feedback isn't allowed
* with GL_PATCHES, so it can't be used.
*
* More investigation showed that the idea of transform feedback after
* a tess control shader was dropped, because some hw vendors couldn't
* support tessellation without a tess eval shader, but the linker section
* wasn't updated to reflect that.
*
* All specifications (ARB_tessellation_shader, GL 4.0-4.5) have this
* spec bug.
*
* Do what's reasonable and always require a tess eval shader if a tess
* control shader is present.
*/
if (num_shaders[MESA_SHADER_TESS_CTRL] > 0 &&
num_shaders[MESA_SHADER_TESS_EVAL] == 0 &&
!prog->SeparateShader) {
linker_error(prog, "Tessellation control shader must be linked with "
"tessellation evaluation shader\n");
goto done;
}
/* Compute shaders have additional restrictions. */
if (num_shaders[MESA_SHADER_COMPUTE] > 0 &&
@ -2982,6 +3258,8 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
if (!prog->LinkStatus)
goto done;
resize_tes_inputs(ctx, prog);
/* Validate the inputs of each stage with the output of the preceding
* stage.
*/