[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