On 07/28/2013 11:03 PM, Paul Berry wrote:
From: Bryan Cain <bryanca...@gmail.com>

This commit adds all of the parsing and semantics for GLSL 150 style
geometry shaders.

v2 (Paul Berry <stereotype...@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 <bryanca...@gmail.com>): fix indexing order of 2D
arrays.  Also, allow interpolation qualifiers in geometry shaders.

v4 (Paul Berry <stereotype...@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;

We should use the undecorated names.

+   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);
     }
-

Spurious (and not necessarily correct) whitespace change.

     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 *, ...);
+

Shouldn't this already come form linker.h?

  /**
   * 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.
+       */

Can this actually occur? It seems like the previous check and the existing checks against var->type->length would catch this case. If it can occur, is there a test? :) Hmm... is it only when size == 0?

+      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);


_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to