[Mesa-dev] [PATCH 11/34] glsl: support compilation of geometry shaders
Paul Berry
stereotype441 at gmail.com
Sun Jul 28 23:03:37 PDT 2013
From: Bryan Cain <bryancain3 at gmail.com>
This commit adds all of the parsing and semantics for GLSL 150 style
geometry shaders.
v2 (Paul Berry <stereotype441 at gmail.com>): Adjust i965's
brw_link_shader() to pass the new is_geometry_shader argument to
do_set_program_inouts. Add a few missing calls to
get_pipeline_stage(). Fix some signed/unsigned comparison warnings.
Fix handling of NULL consumer in assign_varying_locations().
v3 (Bryan Cain <bryancain3 at gmail.com>): fix indexing order of 2D
arrays. Also, allow interpolation qualifiers in geometry shaders.
v4 (Paul Berry <stereotype441 at gmail.com>): Eliminate
get_pipeline_stage()--it is no longer needed thanks to 030ca23 (mesa:
renumber shader indices according to their placement in pipeline).
Remove 2D stuff. Move vertices_per_prim() to ir.h, so that it will be
accessible from outside the linker. Remove
inject_num_vertices_visitor. Move use of geom_array_resize_visitor
prior to use of array_resizing_visitor. Rework for GLSL 1.50. Use a
single shader_type argument for do_set_program_inouts()
---
src/glsl/ast_to_hir.cpp | 31 +++++--
src/glsl/ir.cpp | 21 +++++
src/glsl/ir.h | 5 +-
src/glsl/ir_set_program_inouts.cpp | 86 +++++++++++++-----
src/glsl/linker.cpp | 135 +++++++++++++++++++++++++++--
src/mesa/drivers/dri/i965/brw_shader.cpp | 3 +-
src/mesa/main/mtypes.h | 4 +-
src/mesa/program/ir_to_mesa.cpp | 2 +-
src/mesa/state_tracker/st_glsl_to_tgsi.cpp | 2 +-
9 files changed, 253 insertions(+), 36 deletions(-)
diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp
index 2569cde..431a13d 100644
--- a/src/glsl/ast_to_hir.cpp
+++ b/src/glsl/ast_to_hir.cpp
@@ -2056,11 +2056,11 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
var->interpolation = INTERP_QUALIFIER_NONE;
if (var->interpolation != INTERP_QUALIFIER_NONE &&
- !(state->target == vertex_shader && var->mode == ir_var_shader_out) &&
- !(state->target == fragment_shader && var->mode == ir_var_shader_in)) {
+ ((state->target == vertex_shader && var->mode == ir_var_shader_in) ||
+ (state->target == fragment_shader && var->mode == ir_var_shader_out))) {
_mesa_glsl_error(loc, state,
- "interpolation qualifier `%s' can only be applied to "
- "vertex shader outputs and fragment shader inputs",
+ "interpolation qualifier `%s' cannot be applied to "
+ "vertex shader inputs or fragment shader outputs",
var->interpolation_string());
}
@@ -2662,6 +2662,27 @@ ast_declarator_list::hir(exec_list *instructions,
var = new(ctx) ir_variable(var_type, decl->identifier, ir_var_auto);
+ /* The 'varying in' and 'varying out' qualifiers can only be used with
+ * ARB_geometry_shader4 and EXT_geometry_shader4, which we don't support
+ * yet.
+ */
+ if (this->type->qualifier.flags.q.varying) {
+ if (this->type->qualifier.flags.q.in) {
+ _mesa_glsl_error(& loc, state,
+ "`varying in' qualifier in declaration of "
+ "`%s' only valid for geometry shaders using "
+ "ARB_geometry_shader4 or EXT_geometry_shader4",
+ decl->identifier);
+ }
+ else if (this->type->qualifier.flags.q.out) {
+ _mesa_glsl_error(& loc, state,
+ "`varying out' qualifier in declaration of "
+ "`%s' only valid for geometry shaders using "
+ "ARB_geometry_shader4 or EXT_geometry_shader4",
+ decl->identifier);
+ }
+ }
+
/* From page 22 (page 28 of the PDF) of the GLSL 1.10 specification;
*
* "Global variables can only use the qualifiers const,
@@ -2906,7 +2927,7 @@ ast_declarator_list::hir(exec_list *instructions,
}
break;
default:
- assert(0);
+ break;
}
}
diff --git a/src/glsl/ir.cpp b/src/glsl/ir.cpp
index dad58de..1a3983e 100644
--- a/src/glsl/ir.cpp
+++ b/src/glsl/ir.cpp
@@ -1778,3 +1778,24 @@ ir_rvalue::as_rvalue_to_saturate()
return NULL;
}
+
+
+unsigned
+vertices_per_prim(GLenum prim)
+{
+ switch (prim) {
+ case GL_POINTS:
+ return 1;
+ case GL_LINES:
+ return 2;
+ case GL_TRIANGLES:
+ return 3;
+ case GL_LINES_ADJACENCY_ARB:
+ return 4;
+ case GL_TRIANGLES_ADJACENCY_ARB:
+ return 6;
+ default:
+ assert(!"Bad primitive");
+ return 3;
+ }
+}
diff --git a/src/glsl/ir.h b/src/glsl/ir.h
index ae79a39..af9d77e 100644
--- a/src/glsl/ir.h
+++ b/src/glsl/ir.h
@@ -2112,7 +2112,7 @@ ir_has_call(ir_instruction *ir);
extern void
do_set_program_inouts(exec_list *instructions, struct gl_program *prog,
- bool is_fragment_shader);
+ GLenum shader_type);
extern char *
prototype_string(const glsl_type *return_type, const char *name,
@@ -2128,4 +2128,7 @@ extern void _mesa_print_ir(struct exec_list *instructions,
} /* extern "C" */
#endif
+unsigned
+vertices_per_prim(GLenum prim);
+
#endif /* IR_H */
diff --git a/src/glsl/ir_set_program_inouts.cpp b/src/glsl/ir_set_program_inouts.cpp
index 91a8b45..a578e2a 100644
--- a/src/glsl/ir_set_program_inouts.cpp
+++ b/src/glsl/ir_set_program_inouts.cpp
@@ -44,11 +44,10 @@
class ir_set_program_inouts_visitor : public ir_hierarchical_visitor {
public:
- ir_set_program_inouts_visitor(struct gl_program *prog,
- bool is_fragment_shader)
+ ir_set_program_inouts_visitor(struct gl_program *prog, GLenum shader_type)
{
this->prog = prog;
- this->is_fragment_shader = is_fragment_shader;
+ this->shader_type = shader_type;
}
~ir_set_program_inouts_visitor()
{
@@ -61,7 +60,7 @@ public:
virtual ir_visitor_status visit(ir_dereference_variable *);
struct gl_program *prog;
- bool is_fragment_shader;
+ GLenum shader_type;
};
static inline bool
@@ -112,14 +111,24 @@ ir_set_program_inouts_visitor::visit(ir_dereference_variable *ir)
return visit_continue;
if (ir->type->is_array()) {
- mark(this->prog, ir->var, 0,
- ir->type->length * ir->type->fields.array->matrix_columns,
- this->is_fragment_shader);
+ int matrix_columns = ir->type->fields.array->matrix_columns;
+ int length = ir->type->length;
+ if (this->shader_type == GL_GEOMETRY_SHADER &&
+ ir->var->mode == ir_var_shader_in) {
+ if (ir->type->element_type()->is_array()) {
+ const glsl_type *inner_array_type = ir->type->fields.array;
+ matrix_columns = inner_array_type->fields.array->matrix_columns;
+ length = inner_array_type->length;
+ } else {
+ length = 1;
+ }
+ }
+ mark(this->prog, ir->var, 0, length * matrix_columns,
+ this->shader_type == GL_FRAGMENT_SHADER);
} else {
mark(this->prog, ir->var, 0, ir->type->matrix_columns,
- this->is_fragment_shader);
+ this->shader_type == GL_FRAGMENT_SHADER);
}
-
return visit_continue;
}
@@ -129,7 +138,40 @@ ir_set_program_inouts_visitor::visit_enter(ir_dereference_array *ir)
ir_dereference_variable *deref_var;
ir_constant *index = ir->array_index->as_constant();
deref_var = ir->array->as_dereference_variable();
- ir_variable *var = deref_var ? deref_var->var : NULL;
+ ir_variable *var;
+ bool is_vert_array = false, is_2D_array = false;
+
+ /* Check whether this dereference is of a GS input array. These are special
+ * because the array index refers to the index of an input vertex instead of
+ * the attribute index. The exceptions to this exception are 2D arrays
+ * such as gl_TexCoordIn. For these, there is a nested dereference_array,
+ * where the inner index specifies the vertex and the outer index specifies
+ * the attribute. To complicate things further, matrix columns are also
+ * accessed with dereference_array. So we have to correctly handle 1D
+ * arrays of non-matrices, 1D arrays of matrices, 2D arrays of non-matrices,
+ * and 2D arrays of matrices.
+ */
+ if (this->shader_type == GL_GEOMETRY_SHADER) {
+ if (!deref_var) {
+ /* Either an outer (attribute) dereference of a 2D array or a column
+ * dereference of an array of matrices. */
+ ir_dereference_array *inner_deref = ir->array->as_dereference_array();
+ assert(inner_deref);
+ deref_var = inner_deref->array->as_dereference_variable();
+ is_2D_array = true;
+ }
+
+ if (deref_var && deref_var->var->mode == ir_var_shader_in) {
+ if (ir->type->is_array())
+ /* Inner (vertex) dereference of a 2D array */
+ return visit_continue;
+ else
+ /* Dereference of a 1D (vertex) array */
+ is_vert_array = true;
+ }
+ }
+
+ var = deref_var ? deref_var->var : NULL;
/* Check that we're dereferencing a shader in or out */
if (!var || !is_shader_inout(var))
@@ -137,14 +179,17 @@ ir_set_program_inouts_visitor::visit_enter(ir_dereference_array *ir)
if (index) {
int width = 1;
+ const glsl_type *type = is_vert_array ?
+ deref_var->type->fields.array : deref_var->type;
+ int offset = is_vert_array && !is_2D_array ? 0 : index->value.i[0];
- if (deref_var->type->is_array() &&
- deref_var->type->fields.array->is_matrix()) {
- width = deref_var->type->fields.array->matrix_columns;
+ if (type->is_array() &&
+ type->fields.array->is_matrix()) {
+ width = type->fields.array->matrix_columns;
}
- mark(this->prog, var, index->value.i[0] * width, width,
- this->is_fragment_shader);
+ mark(this->prog, var, offset * width, width,
+ this->shader_type == GL_FRAGMENT_SHADER);
return visit_continue_with_parent;
}
@@ -164,7 +209,8 @@ ir_set_program_inouts_visitor::visit_enter(ir_function_signature *ir)
ir_visitor_status
ir_set_program_inouts_visitor::visit_enter(ir_expression *ir)
{
- if (is_fragment_shader && ir->operation == ir_unop_dFdy) {
+ if (this->shader_type == GL_FRAGMENT_SHADER &&
+ ir->operation == ir_unop_dFdy) {
gl_fragment_program *fprog = (gl_fragment_program *) prog;
fprog->UsesDFdy = true;
}
@@ -175,7 +221,7 @@ ir_visitor_status
ir_set_program_inouts_visitor::visit_enter(ir_discard *)
{
/* discards are only allowed in fragment shaders. */
- assert(is_fragment_shader);
+ assert(this->shader_type == GL_FRAGMENT_SHADER);
gl_fragment_program *fprog = (gl_fragment_program *) prog;
fprog->UsesKill = true;
@@ -185,14 +231,14 @@ ir_set_program_inouts_visitor::visit_enter(ir_discard *)
void
do_set_program_inouts(exec_list *instructions, struct gl_program *prog,
- bool is_fragment_shader)
+ GLenum shader_type)
{
- ir_set_program_inouts_visitor v(prog, is_fragment_shader);
+ ir_set_program_inouts_visitor v(prog, shader_type);
prog->InputsRead = 0;
prog->OutputsWritten = 0;
prog->SystemValuesRead = 0;
- if (is_fragment_shader) {
+ if (shader_type == GL_FRAGMENT_SHADER) {
gl_fragment_program *fprog = (gl_fragment_program *) prog;
memset(fprog->InterpQualifier, 0, sizeof(fprog->InterpQualifier));
fprog->IsCentroid = 0;
diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
index 7192567..70d9cf4 100644
--- a/src/glsl/linker.cpp
+++ b/src/glsl/linker.cpp
@@ -73,11 +73,14 @@
#include "linker.h"
#include "link_varyings.h"
#include "ir_optimization.h"
+#include "ir_rvalue_visitor.h"
extern "C" {
#include "main/shaderobj.h"
}
+void linker_error(gl_shader_program *, const char *, ...);
+
/**
* Visitor that determines whether or not a variable is ever written.
*/
@@ -174,6 +177,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
linker_error(gl_shader_program *prog, const char *fmt, ...)
{
@@ -437,6 +511,24 @@ validate_fragment_shader_executable(struct gl_shader_program *prog,
}
}
+/**
+ * Verify that a geometry shader executable meets all semantic requirements
+ *
+ * Also sets prog->Geom.VerticesIn as a side effect.
+ *
+ * \param shader Geometry shader executable to be verified
+ */
+void
+validate_geometry_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
+{
+ if (shader == NULL)
+ return;
+
+ unsigned num_vertices = vertices_per_prim(prog->Geom.InputType);
+ prog->Geom.VerticesIn = num_vertices;
+}
+
/**
* Generate a string describing the mode of a variable
@@ -1091,6 +1183,16 @@ link_intrastage_shaders(void *mem_ctx,
if (linked)
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
* unspecified sizes have a size specified. The size is inferred from the
* max_array_access field.
@@ -1648,10 +1750,13 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
unsigned num_vert_shaders = 0;
struct gl_shader **frag_shader_list;
unsigned num_frag_shaders = 0;
+ struct gl_shader **geom_shader_list;
+ unsigned num_geom_shaders = 0;
vert_shader_list = (struct gl_shader **)
- calloc(2 * prog->NumShaders, sizeof(struct gl_shader *));
+ calloc(3 * prog->NumShaders, sizeof(struct gl_shader *));
frag_shader_list = &vert_shader_list[prog->NumShaders];
+ geom_shader_list = &vert_shader_list[prog->NumShaders * 2];
unsigned min_version = UINT_MAX;
unsigned max_version = 0;
@@ -1677,8 +1782,8 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
num_frag_shaders++;
break;
case GL_GEOMETRY_SHADER:
- /* FINISHME: Support geometry shaders. */
- assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER);
+ geom_shader_list[num_geom_shaders] = prog->Shaders[i];
+ num_geom_shaders++;
break;
}
}
@@ -1740,6 +1845,22 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
sh);
}
+ if (num_geom_shaders > 0) {
+ gl_shader *const sh =
+ link_intrastage_shaders(mem_ctx, ctx, prog, geom_shader_list,
+ num_geom_shaders);
+
+ if (!prog->LinkStatus)
+ goto done;
+
+ validate_geometry_shader_executable(prog, sh);
+ if (!prog->LinkStatus)
+ goto done;
+
+ _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_GEOMETRY],
+ sh);
+ }
+
/* Here begins the inter-stage linking phase. Some initial validation is
* performed, then locations are assigned for uniforms, attributes, and
* varyings.
@@ -1826,7 +1947,11 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
prog->_LinkedShaders[MESA_SHADER_VERTEX],
VERT_ATTRIB_GENERIC0, VARYING_SLOT_VAR0);
}
- /* FINISHME: Geometry shaders not implemented yet */
+ if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
+ link_invalidate_variable_locations(
+ prog->_LinkedShaders[MESA_SHADER_GEOMETRY],
+ VARYING_SLOT_VAR0, VARYING_SLOT_VAR0);
+ }
if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) {
link_invalidate_variable_locations(
prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
@@ -1860,7 +1985,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
* non-zero, but the program object has no vertex or geometry
* shader;
*/
- if (first >= MESA_SHADER_FRAGMENT) {
+ if (first == MESA_SHADER_FRAGMENT) {
linker_error(prog, "Transform feedback varyings specified, but "
"no vertex or geometry shader is present.");
goto done;
diff --git a/src/mesa/drivers/dri/i965/brw_shader.cpp b/src/mesa/drivers/dri/i965/brw_shader.cpp
index 3322e80..418ea9b 100644
--- a/src/mesa/drivers/dri/i965/brw_shader.cpp
+++ b/src/mesa/drivers/dri/i965/brw_shader.cpp
@@ -236,8 +236,7 @@ brw_link_shader(struct gl_context *ctx, struct gl_shader_program *shProg)
reparent_ir(shader->ir, shader->ir);
ralloc_free(mem_ctx);
- do_set_program_inouts(shader->ir, prog,
- shader->base.Type == GL_FRAGMENT_SHADER);
+ do_set_program_inouts(shader->ir, prog, shader->base.Type);
prog->SamplersUsed = shader->base.active_samplers;
_mesa_update_shader_textures_used(shProg, prog);
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index efa2d39..2725eef 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -1851,7 +1851,7 @@ struct gl_program
GLuint Id;
GLubyte *String; /**< Null-terminated program text */
GLint RefCount;
- GLenum Target; /**< GL_VERTEX/FRAGMENT_PROGRAM_ARB */
+ GLenum Target; /**< GL_VERTEX/FRAGMENT_PROGRAM_ARB, GL_GEOMETRY_PROGRAM_NV */
GLenum Format; /**< String encoding format */
struct prog_instruction *Instructions;
@@ -1918,6 +1918,7 @@ struct gl_geometry_program
{
struct gl_program Base; /**< base class */
+ GLint VerticesIn;
GLint VerticesOut;
GLenum InputType; /**< GL_POINTS, GL_LINES, GL_LINES_ADJACENCY_ARB,
GL_TRIANGLES, or GL_TRIANGLES_ADJACENCY_ARB */
@@ -2320,6 +2321,7 @@ struct gl_shader_program
/** Geometry shader state - copied into gl_geometry_program at link time */
struct {
+ GLint VerticesIn;
GLint VerticesOut;
GLenum InputType; /**< GL_POINTS, GL_LINES, GL_LINES_ADJACENCY_ARB,
GL_TRIANGLES, or GL_TRIANGLES_ADJACENCY_ARB */
diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp
index e526582..914aca4 100644
--- a/src/mesa/program/ir_to_mesa.cpp
+++ b/src/mesa/program/ir_to_mesa.cpp
@@ -2975,7 +2975,7 @@ get_mesa_program(struct gl_context *ctx,
*/
mesa_instructions = NULL;
- do_set_program_inouts(shader->ir, prog, shader->Type == GL_FRAGMENT_SHADER);
+ do_set_program_inouts(shader->ir, prog, shader->Type);
prog->SamplersUsed = shader->active_samplers;
prog->ShadowSamplers = shader->shadow_samplers;
diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
index 77623f9..52e44ad 100644
--- a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
+++ b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
@@ -5128,7 +5128,7 @@ get_mesa_program(struct gl_context *ctx,
prog->Instructions = NULL;
prog->NumInstructions = 0;
- do_set_program_inouts(shader->ir, prog, shader->Type == GL_FRAGMENT_SHADER);
+ do_set_program_inouts(shader->ir, prog, shader->Type);
count_resources(v, prog);
_mesa_reference_program(ctx, &shader->Program, prog);
--
1.8.3.4
More information about the mesa-dev
mailing list