glsl: only update doubles inputs for vertex inputs.
[mesa.git] / src / glsl / ir_set_program_inouts.cpp
index 27c3ca70e5aab39974fcc7b828aa55bdd9e88388..c9b8a38384ff924c6841253b669e73b256b12390 100644 (file)
@@ -27,7 +27,7 @@
  * Sets the InputsRead and OutputsWritten of Mesa programs.
  *
  * Additionally, for fragment shaders, sets the InterpQualifier array, the
- * IsCentroid bitfield, and the UsesDFdy flag.
+ * IsCentroid and IsSample bitfields, and the UsesDFdy flag.
  *
  * Mesa programs (gl_program, not gl_shader_program) have a set of
  * flags indicating which varyings are read and written.  Computing
 #include "ir_visitor.h"
 #include "glsl_types.h"
 
+namespace {
+
 class ir_set_program_inouts_visitor : public ir_hierarchical_visitor {
 public:
-   ir_set_program_inouts_visitor(struct gl_program *prog, GLenum shader_type)
+   ir_set_program_inouts_visitor(struct gl_program *prog,
+                                 gl_shader_stage shader_stage)
    {
       this->prog = prog;
-      this->shader_type = shader_type;
+      this->shader_stage = shader_stage;
    }
    ~ir_set_program_inouts_visitor()
    {
@@ -57,6 +60,7 @@ public:
    virtual ir_visitor_status visit_enter(ir_function_signature *);
    virtual ir_visitor_status visit_enter(ir_expression *);
    virtual ir_visitor_status visit_enter(ir_discard *);
+   virtual ir_visitor_status visit_enter(ir_texture *);
    virtual ir_visitor_status visit(ir_dereference_variable *);
 
 private:
@@ -64,20 +68,22 @@ private:
    bool try_mark_partial_variable(ir_variable *var, ir_rvalue *index);
 
    struct gl_program *prog;
-   GLenum shader_type;
+   gl_shader_stage shader_stage;
 };
 
+} /* anonymous namespace */
+
 static inline bool
 is_shader_inout(ir_variable *var)
 {
-   return var->mode == ir_var_shader_in ||
-          var->mode == ir_var_shader_out ||
-          var->mode == ir_var_system_value;
+   return var->data.mode == ir_var_shader_in ||
+          var->data.mode == ir_var_shader_out ||
+          var->data.mode == ir_var_system_value;
 }
 
 static void
 mark(struct gl_program *prog, ir_variable *var, int offset, int len,
-     bool is_fragment_shader)
+     gl_shader_stage stage)
 {
    /* As of GLSL 1.20, varyings can only be floats, floating-point
     * vectors or matrices, or arrays of them.  For Mesa programs using
@@ -88,21 +94,49 @@ mark(struct gl_program *prog, ir_variable *var, int offset, int len,
     */
 
    for (int i = 0; i < len; i++) {
-      GLbitfield64 bitfield = BITFIELD64_BIT(var->location + var->index + offset + i);
-      if (var->mode == ir_var_shader_in) {
-        prog->InputsRead |= bitfield;
-         if (is_fragment_shader) {
+      int idx = var->data.location + var->data.index + offset + i;
+      bool is_patch_generic = var->data.patch &&
+                              idx != VARYING_SLOT_TESS_LEVEL_INNER &&
+                              idx != VARYING_SLOT_TESS_LEVEL_OUTER;
+      GLbitfield64 bitfield;
+
+      if (is_patch_generic) {
+         assert(idx >= VARYING_SLOT_PATCH0 && idx < VARYING_SLOT_TESS_MAX);
+         bitfield = BITFIELD64_BIT(idx - VARYING_SLOT_PATCH0);
+      }
+      else {
+         assert(idx < VARYING_SLOT_MAX);
+         bitfield = BITFIELD64_BIT(idx);
+      }
+
+      if (var->data.mode == ir_var_shader_in) {
+         if (is_patch_generic)
+            prog->PatchInputsRead |= bitfield;
+         else
+            prog->InputsRead |= bitfield;
+
+         /* double inputs read is only for vertex inputs */
+         if (stage == MESA_SHADER_VERTEX &&
+             var->type->without_array()->is_dual_slot_double())
+            prog->DoubleInputsRead |= bitfield;
+
+         if (stage == MESA_SHADER_FRAGMENT) {
             gl_fragment_program *fprog = (gl_fragment_program *) prog;
-            fprog->InterpQualifier[var->location + var->index + offset + i] =
-               (glsl_interp_qualifier) var->interpolation;
-            if (var->centroid)
+            fprog->InterpQualifier[idx] =
+               (glsl_interp_qualifier) var->data.interpolation;
+            if (var->data.centroid)
                fprog->IsCentroid |= bitfield;
+            if (var->data.sample)
+               fprog->IsSample |= bitfield;
          }
-      } else if (var->mode == ir_var_system_value) {
+      } else if (var->data.mode == ir_var_system_value) {
          prog->SystemValuesRead |= bitfield;
       } else {
-         assert(var->mode == ir_var_shader_out);
-        prog->OutputsWritten |= bitfield;
+         assert(var->data.mode == ir_var_shader_out);
+         if (is_patch_generic)
+            prog->PatchOutputsWritten |= bitfield;
+         else
+            prog->OutputsWritten |= bitfield;
       }
    }
 }
@@ -114,8 +148,37 @@ mark(struct gl_program *prog, ir_variable *var, int offset, int len,
 void
 ir_set_program_inouts_visitor::mark_whole_variable(ir_variable *var)
 {
-   mark(this->prog, var, 0, var->type->count_attribute_slots(),
-        this->shader_type == GL_FRAGMENT_SHADER);
+   const glsl_type *type = var->type;
+   bool vertex_input = false;
+   if (this->shader_stage == MESA_SHADER_GEOMETRY &&
+       var->data.mode == ir_var_shader_in && type->is_array()) {
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_TESS_CTRL &&
+       var->data.mode == ir_var_shader_in) {
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_TESS_CTRL &&
+       var->data.mode == ir_var_shader_out && !var->data.patch) {
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_TESS_EVAL &&
+       var->data.mode == ir_var_shader_in && !var->data.patch) {
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_VERTEX &&
+       var->data.mode == ir_var_shader_in)
+      vertex_input = true;
+
+   mark(this->prog, var, 0, type->count_attribute_slots(vertex_input),
+        this->shader_stage);
 }
 
 /* Default handler: Mark all the locations in the variable as used. */
@@ -133,7 +196,14 @@ ir_set_program_inouts_visitor::visit(ir_dereference_variable *ir)
 /**
  * Try to mark a portion of the given variable as used.  Caller must ensure
  * that the variable represents a shader input or output which can be indexed
- * into in array fashion (an array or matrix).
+ * into in array fashion (an array or matrix).  For the purpose of geometry
+ * shader inputs (which are always arrays*), this means that the array element
+ * must be something that can be indexed into in array fashion.
+ *
+ * *Except gl_PrimitiveIDIn, as noted below.
+ *
+ * For tessellation control shaders all inputs and non-patch outputs are
+ * arrays. For tessellation evaluation shaders non-patch inputs are arrays.
  *
  * If the index can't be interpreted as a constant, or some other problem
  * occurs, then nothing will be marked and false will be returned.
@@ -144,6 +214,40 @@ ir_set_program_inouts_visitor::try_mark_partial_variable(ir_variable *var,
 {
    const glsl_type *type = var->type;
 
+   if (this->shader_stage == MESA_SHADER_GEOMETRY &&
+       var->data.mode == ir_var_shader_in) {
+      /* The only geometry shader input that is not an array is
+       * gl_PrimitiveIDIn, and in that case, this code will never be reached,
+       * because gl_PrimitiveIDIn can't be indexed into in array fashion.
+       */
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_TESS_CTRL &&
+       var->data.mode == ir_var_shader_in) {
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_TESS_CTRL &&
+       var->data.mode == ir_var_shader_out && !var->data.patch) {
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   if (this->shader_stage == MESA_SHADER_TESS_EVAL &&
+       var->data.mode == ir_var_shader_in && !var->data.patch) {
+      assert(type->is_array());
+      type = type->fields.array;
+   }
+
+   /* TODO: implement proper arrays of arrays support
+    * for now let the caller mark whole variable as used.
+    */
+   if (type->is_array() && type->fields.array->is_array())
+      return false;
+
    /* The code below only handles:
     *
     * - Indexing into matrices
@@ -198,24 +302,81 @@ ir_set_program_inouts_visitor::try_mark_partial_variable(ir_variable *var,
    }
 
    mark(this->prog, var, index_as_constant->value.u[0] * elem_width,
-        elem_width, this->shader_type == GL_FRAGMENT_SHADER);
+        elem_width, this->shader_stage);
    return true;
 }
 
-ir_visitor_status
-ir_set_program_inouts_visitor::visit_enter(ir_dereference_array *ir)
+static bool
+is_multiple_vertices(gl_shader_stage stage, ir_variable *var)
 {
-   ir_dereference_variable *deref_var;
-   deref_var = ir->array->as_dereference_variable();
-   ir_variable *var = deref_var ? deref_var->var : NULL;
+   if (var->data.patch)
+      return false;
 
-   /* Check that we're dereferencing a shader in or out */
-   if (!var || !is_shader_inout(var))
-      return visit_continue;
+   if (var->data.mode == ir_var_shader_in)
+      return stage == MESA_SHADER_GEOMETRY ||
+             stage == MESA_SHADER_TESS_CTRL ||
+             stage == MESA_SHADER_TESS_EVAL;
+   if (var->data.mode == ir_var_shader_out)
+      return stage == MESA_SHADER_TESS_CTRL;
+
+   return false;
+}
 
-   if (try_mark_partial_variable(var, ir->array_index))
-      return visit_continue_with_parent;
+ir_visitor_status
+ir_set_program_inouts_visitor::visit_enter(ir_dereference_array *ir)
+{
+   /* Note: for geometry shader inputs, lower_named_interface_blocks may
+    * create 2D arrays, so we need to be able to handle those.  2D arrays
+    * shouldn't be able to crop up for any other reason.
+    */
+   if (ir_dereference_array * const inner_array =
+       ir->array->as_dereference_array()) {
+      /*          ir => foo[i][j]
+       * inner_array => foo[i]
+       */
+      if (ir_dereference_variable * const deref_var =
+          inner_array->array->as_dereference_variable()) {
+         if (is_multiple_vertices(this->shader_stage, deref_var->var)) {
+            /* foo is a geometry or tessellation shader input, so i is
+             * the vertex, and j the part of the input we're accessing.
+             */
+            if (try_mark_partial_variable(deref_var->var, ir->array_index))
+            {
+               /* We've now taken care of foo and j, but i might contain a
+                * subexpression that accesses shader inputs.  So manually
+                * visit i and then continue with the parent.
+                */
+               inner_array->array_index->accept(this);
+               return visit_continue_with_parent;
+            }
+         }
+      }
+   } else if (ir_dereference_variable * const deref_var =
+              ir->array->as_dereference_variable()) {
+      /* ir => foo[i], where foo is a variable. */
+      if (is_multiple_vertices(this->shader_stage, deref_var->var)) {
+         /* foo is a geometry or tessellation shader input, so i is
+          * the vertex, and we're accessing the entire input.
+          */
+         mark_whole_variable(deref_var->var);
+         /* We've now taken care of foo, but i might contain a subexpression
+          * that accesses shader inputs.  So manually visit i and then
+          * continue with the parent.
+          */
+         ir->array_index->accept(this);
+         return visit_continue_with_parent;
+      } else if (is_shader_inout(deref_var->var)) {
+         /* foo is a shader input/output, but not a geometry shader input,
+          * so i is the part of the input we're accessing.
+          */
+         if (try_mark_partial_variable(deref_var->var, ir->array_index))
+            return visit_continue_with_parent;
+      }
+   }
 
+   /* The expression is something we don't recognize.  Just visit its
+    * subexpressions.
+    */
    return visit_continue;
 }
 
@@ -232,8 +393,10 @@ 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 (this->shader_type == GL_FRAGMENT_SHADER &&
-       ir->operation == ir_unop_dFdy) {
+   if (this->shader_stage == MESA_SHADER_FRAGMENT &&
+       (ir->operation == ir_unop_dFdy ||
+        ir->operation == ir_unop_dFdy_coarse ||
+        ir->operation == ir_unop_dFdy_fine)) {
       gl_fragment_program *fprog = (gl_fragment_program *) prog;
       fprog->UsesDFdy = true;
    }
@@ -244,7 +407,7 @@ ir_visitor_status
 ir_set_program_inouts_visitor::visit_enter(ir_discard *)
 {
    /* discards are only allowed in fragment shaders. */
-   assert(this->shader_type == GL_FRAGMENT_SHADER);
+   assert(this->shader_stage == MESA_SHADER_FRAGMENT);
 
    gl_fragment_program *fprog = (gl_fragment_program *) prog;
    fprog->UsesKill = true;
@@ -252,19 +415,30 @@ ir_set_program_inouts_visitor::visit_enter(ir_discard *)
    return visit_continue;
 }
 
+ir_visitor_status
+ir_set_program_inouts_visitor::visit_enter(ir_texture *ir)
+{
+   if (ir->op == ir_tg4)
+      prog->UsesGather = true;
+   return visit_continue;
+}
+
 void
 do_set_program_inouts(exec_list *instructions, struct gl_program *prog,
-                      GLenum shader_type)
+                      gl_shader_stage shader_stage)
 {
-   ir_set_program_inouts_visitor v(prog, shader_type);
+   ir_set_program_inouts_visitor v(prog, shader_stage);
 
    prog->InputsRead = 0;
    prog->OutputsWritten = 0;
+   prog->PatchInputsRead = 0;
+   prog->PatchOutputsWritten = 0;
    prog->SystemValuesRead = 0;
-   if (shader_type == GL_FRAGMENT_SHADER) {
+   if (shader_stage == MESA_SHADER_FRAGMENT) {
       gl_fragment_program *fprog = (gl_fragment_program *) prog;
       memset(fprog->InterpQualifier, 0, sizeof(fprog->InterpQualifier));
       fprog->IsCentroid = 0;
+      fprog->IsSample = 0;
       fprog->UsesDFdy = false;
       fprog->UsesKill = false;
    }