mesa: Fix transform feedback of unsubscripted gl_ClipDistance array.
authorPaul Berry <stereotype441@gmail.com>
Wed, 4 Jan 2012 21:57:52 +0000 (13:57 -0800)
committerPaul Berry <stereotype441@gmail.com>
Wed, 11 Jan 2012 15:57:56 +0000 (07:57 -0800)
On drivers that set gl_shader_compiler_options::LowerClipDistance (for
example i965), we need to handle transform feedback of gl_ClipDistance
specially, to account for the fact that the hardware represents it as
an array of vec4's rather than an array of floats.

The previous way this was accounted for (translating the request for
gl_ClipDistance[n] to a request for a component of
gl_ClipDistanceMESA[n/4]) doesn't work when performing transform
feedback on the whole unsubscripted array, because we need to keep
track of the size of the gl_ClipDistance array prior to the lowering
pass.  So I replaced it with a boolean is_clip_distance_mesa, which
switches on the special logic that is needed to handle the lowered
version of gl_ClipDistance.

Fixes Piglit tests "EXT_transform_feedback/builtin-varyings
gl_ClipDistance[{1,2,3,5,6,7}]-no-subscript".

Reviewed-by: Eric Anholt <eric@anholt.net>
src/glsl/linker.cpp
src/mesa/main/mtypes.h

index f32c2175f5945125f47390c4a212ef453b3771af..39a9c466499e6f9a12bb7ee49c6bcefc562f2cb6 100644 (file)
@@ -246,7 +246,8 @@ count_attribute_slots(const glsl_type *t)
 /**
  * Verify that a vertex shader executable meets all semantic requirements.
  *
- * Also sets prog->Vert.UsesClipDistance as a side effect.
+ * Also sets prog->Vert.UsesClipDistance and prog->Vert.ClipDistanceArraySize
+ * as a side effect.
  *
  * \param shader  Vertex shader executable to be verified
  */
@@ -264,6 +265,8 @@ validate_vertex_shader_executable(struct gl_shader_program *prog,
       return false;
    }
 
+   prog->Vert.ClipDistanceArraySize = 0;
+
    if (prog->Version >= 130) {
       /* From section 7.1 (Vertex Shader Special Variables) of the
        * GLSL 1.30 spec:
@@ -282,6 +285,10 @@ validate_vertex_shader_executable(struct gl_shader_program *prog,
          return false;
       }
       prog->Vert.UsesClipDistance = clip_distance.variable_found();
+      ir_variable *clip_distance_var =
+         shader->symbols->get_variable("gl_ClipDistance");
+      if (clip_distance_var)
+         prog->Vert.ClipDistanceArraySize = clip_distance_var->type->length;
    }
 
    return true;
@@ -1399,7 +1406,10 @@ public:
     */
    bool matches_var(ir_variable *var) const
    {
-      return strcmp(var->name, this->var_name) == 0;
+      if (this->is_clip_distance_mesa)
+         return strcmp(var->name, "gl_ClipDistanceMESA") == 0;
+      else
+         return strcmp(var->name, this->var_name) == 0;
    }
 
    /**
@@ -1408,10 +1418,10 @@ public:
     */
    unsigned num_components() const
    {
-      if (this->single_component == -1)
-         return this->vector_elements * this->matrix_columns * this->size;
+      if (this->is_clip_distance_mesa)
+         return this->size;
       else
-         return 1;
+         return this->vector_elements * this->matrix_columns * this->size;
    }
 
 private:
@@ -1437,11 +1447,10 @@ private:
    unsigned array_subscript;
 
    /**
-    * Which component to extract from the vertex shader output location that
-    * the linker assigned to this variable.  -1 if all components should be
-    * extracted.
+    * True if the variable is gl_ClipDistance and the driver lowers
+    * gl_ClipDistance to gl_ClipDistanceMESA.
     */
-   int single_component;
+   bool is_clip_distance_mesa;
 
    /**
     * The vertex shader output location that the linker assigned for this
@@ -1487,7 +1496,7 @@ tfeedback_decl::init(struct gl_context *ctx, struct gl_shader_program *prog,
 
    this->location = -1;
    this->orig_name = input;
-   this->single_component = -1;
+   this->is_clip_distance_mesa = false;
 
    const char *bracket = strrchr(input, '[');
 
@@ -1503,17 +1512,13 @@ tfeedback_decl::init(struct gl_context *ctx, struct gl_shader_program *prog,
       this->is_subscripted = false;
    }
 
-   /* For drivers that lower gl_ClipDistance to gl_ClipDistanceMESA, we need
-    * to convert a request for gl_ClipDistance[n] into a request for a
-    * component of gl_ClipDistanceMESA[n/4].
+   /* For drivers that lower gl_ClipDistance to gl_ClipDistanceMESA, this
+    * class must behave specially to account for the fact that gl_ClipDistance
+    * is converted from a float[8] to a vec4[2].
     */
    if (ctx->ShaderCompilerOptions[MESA_SHADER_VERTEX].LowerClipDistance &&
        strcmp(this->var_name, "gl_ClipDistance") == 0) {
-      this->var_name = "gl_ClipDistanceMESA";
-      if (this->is_subscripted) {
-         this->single_component = this->array_subscript % 4;
-         this->array_subscript /= 4;
-      }
+      this->is_clip_distance_mesa = true;
    }
 
    return true;
@@ -1533,8 +1538,6 @@ tfeedback_decl::is_same(const tfeedback_decl &x, const tfeedback_decl &y)
       return false;
    if (x.is_subscripted && x.array_subscript != y.array_subscript)
       return false;
-   if (x.single_component != y.single_component)
-      return false;
    return true;
 }
 
@@ -1555,27 +1558,36 @@ tfeedback_decl::assign_location(struct gl_context *ctx,
       /* Array variable */
       const unsigned matrix_cols =
          output_var->type->fields.array->matrix_columns;
+      unsigned actual_array_size = this->is_clip_distance_mesa ?
+         prog->Vert.ClipDistanceArraySize : output_var->type->array_size();
 
       if (this->is_subscripted) {
          /* Check array bounds. */
-         if (this->array_subscript >=
-             (unsigned) output_var->type->array_size()) {
+         if (this->array_subscript >= actual_array_size) {
             linker_error(prog, "Transform feedback varying %s has index "
-                         "%i, but the array size is %i.",
+                         "%i, but the array size is %u.",
                          this->orig_name, this->array_subscript,
-                         output_var->type->array_size());
+                         actual_array_size);
             return false;
          }
-         this->location =
-            output_var->location + this->array_subscript * matrix_cols;
+         if (this->is_clip_distance_mesa) {
+            this->location =
+               output_var->location + this->array_subscript / 4;
+         } else {
+            this->location =
+               output_var->location + this->array_subscript * matrix_cols;
+         }
          this->size = 1;
       } else {
          this->location = output_var->location;
-         this->size = (unsigned) output_var->type->array_size();
+         this->size = actual_array_size;
       }
       this->vector_elements = output_var->type->fields.array->vector_elements;
       this->matrix_columns = matrix_cols;
-      this->type = output_var->type->fields.array->gl_type;
+      if (this->is_clip_distance_mesa)
+         this->type = GL_FLOAT;
+      else
+         this->type = output_var->type->fields.array->gl_type;
    } else {
       /* Regular variable (scalar, vector, or matrix) */
       if (this->is_subscripted) {
@@ -1590,6 +1602,7 @@ tfeedback_decl::assign_location(struct gl_context *ctx,
       this->matrix_columns = output_var->type->matrix_columns;
       this->type = output_var->type->gl_type;
    }
+
    /* From GL_EXT_transform_feedback:
     *   A program will fail to link if:
     *
@@ -1634,18 +1647,28 @@ tfeedback_decl::store(struct gl_shader_program *prog,
                    this->orig_name);
       return false;
    }
+   unsigned translated_size = this->size;
+   if (this->is_clip_distance_mesa)
+      translated_size = (translated_size + 3) / 4;
    unsigned components_so_far = 0;
-   for (unsigned index = 0; index < this->size; ++index) {
+   for (unsigned index = 0; index < translated_size; ++index) {
       for (unsigned v = 0; v < this->matrix_columns; ++v) {
-         unsigned num_components =
-            this->single_component >= 0 ? 1 : this->vector_elements;
+         unsigned num_components = this->vector_elements;
+         info->Outputs[info->NumOutputs].ComponentOffset = 0;
+         if (this->is_clip_distance_mesa) {
+            if (this->is_subscripted) {
+               num_components = 1;
+               info->Outputs[info->NumOutputs].ComponentOffset =
+                  this->array_subscript % 4;
+            } else {
+               num_components = MIN2(4, this->size - components_so_far);
+            }
+         }
          info->Outputs[info->NumOutputs].OutputRegister =
             this->location + v + index * this->matrix_columns;
          info->Outputs[info->NumOutputs].NumComponents = num_components;
          info->Outputs[info->NumOutputs].OutputBuffer = buffer;
          info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer];
-         info->Outputs[info->NumOutputs].ComponentOffset =
-            this->single_component >= 0 ? this->single_component : 0;
          ++info->NumOutputs;
          info->BufferStride[buffer] += num_components;
          components_so_far += num_components;
index 64d8c8d3f71982e6d3a769d870ff29e640f0a198..bb46ba0c0351fe63c5ea2f7763422072ca7d21ad 100644 (file)
@@ -2277,6 +2277,8 @@ struct gl_shader_program
    /** Vertex shader state - copied into gl_vertex_program at link time */
    struct {
       GLboolean UsesClipDistance; /**< True if gl_ClipDistance is written to. */
+      GLuint ClipDistanceArraySize; /**< Size of the gl_ClipDistance array, or
+                                         0 if not present. */
    } Vert;
 
    /* post-link info: */