glsl: add xfb_stride link time validation
authorTimothy Arceri <timothy.arceri@collabora.com>
Thu, 3 Mar 2016 04:26:53 +0000 (15:26 +1100)
committerTimothy Arceri <timothy.arceri@collabora.com>
Thu, 31 Mar 2016 01:52:05 +0000 (12:52 +1100)
From the ARB_enhanced_layous spec:

   "It is a compile-time or link-time error to have any *xfb_offset*
    that overflows *xfb_stride*, whether stated on declarations before
    or after the *xfb_stride*, or in different compilation units.

    ...

    When no *xfb_stride* is specified for a buffer, the stride of a
    buffer will be the smallest needed to hold the variable placed at
    the highest offset, including any required padding."

Reviewed-by: Dave Airlie <airlied@redhat.com>
src/compiler/glsl/link_varyings.cpp
src/compiler/glsl/link_varyings.h

index d91642dea5ce39ac415daf06f63beeea9d0488c9..89bc68e277e7fd1841e3c6fec5827ba7f89d3527 100644 (file)
@@ -716,7 +716,7 @@ bool
 tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog,
                       struct gl_transform_feedback_info *info,
                       unsigned buffer, const unsigned max_outputs,
-                      bool has_xfb_qualifiers) const
+                      bool *explicit_stride, bool has_xfb_qualifiers) const
 {
    assert(!this->next_buffer_separator);
 
@@ -726,6 +726,13 @@ tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog,
       return true;
    }
 
+   unsigned xfb_offset = 0;
+   if (has_xfb_qualifiers) {
+      xfb_offset = this->offset / 4;
+   } else {
+      xfb_offset = info->Buffers[buffer].Stride;
+   }
+
    /* From GL_EXT_transform_feedback:
     *   A program will fail to link if:
     *
@@ -752,17 +759,38 @@ tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog,
       info->Outputs[info->NumOutputs].NumComponents = output_size;
       info->Outputs[info->NumOutputs].StreamId = stream_id;
       info->Outputs[info->NumOutputs].OutputBuffer = buffer;
-      info->Outputs[info->NumOutputs].DstOffset =
-         info->Buffers[buffer].Stride;
+      info->Outputs[info->NumOutputs].DstOffset = xfb_offset;
       ++info->NumOutputs;
-      info->Buffers[buffer].Stride += output_size;
       info->Buffers[buffer].Stream = this->stream_id;
+      xfb_offset += output_size;
+
       num_components -= output_size;
       location++;
       location_frac = 0;
    }
 
-   info->Varyings[info->NumVarying].Name = ralloc_strdup(prog, this->orig_name);
+   if (explicit_stride && explicit_stride[buffer]) {
+      if (this->is_double() && info->Buffers[buffer].Stride % 2) {
+         linker_error(prog, "invalid qualifier xfb_stride=%d must be a "
+                      "multiple of 8 as its applied to a type that is or "
+                      "contains a double.",
+                      info->Buffers[buffer].Stride * 4);
+         return false;
+      }
+
+      if ((this->offset / 4) / info->Buffers[buffer].Stride !=
+          (xfb_offset - 1) / info->Buffers[buffer].Stride) {
+         linker_error(prog, "xfb_offset (%d) overflows xfb_stride (%d) for "
+                      "buffer (%d)", xfb_offset * 4,
+                      info->Buffers[buffer].Stride * 4, buffer);
+         return false;
+      }
+   } else {
+      info->Buffers[buffer].Stride = xfb_offset;
+   }
+
+   info->Varyings[info->NumVarying].Name = ralloc_strdup(prog,
+                                                         this->orig_name);
    info->Varyings[info->NumVarying].Type = this->type;
    info->Varyings[info->NumVarying].Size = this->size;
    info->NumVarying++;
@@ -916,7 +944,7 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,
       /* GL_SEPARATE_ATTRIBS */
       for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
          if (!tfeedback_decls[i].store(ctx, prog, &prog->LinkedTransformFeedback,
-                                       num_buffers, num_outputs,
+                                       num_buffers, num_outputs, NULL,
                                        has_xfb_qualifiers))
             return false;
 
@@ -929,12 +957,14 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,
       int buffer_stream_id = -1;
       unsigned buffer =
          num_tfeedback_decls ? tfeedback_decls[0].get_buffer() : 0;
+      bool explicit_stride[MAX_FEEDBACK_BUFFERS] = { false };
 
       /* Apply any xfb_stride global qualifiers */
       if (has_xfb_qualifiers) {
          for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
             if (prog->TransformFeedback.BufferStride[j]) {
                buffers |= 1 << j;
+               explicit_stride[j] = true;
                prog->LinkedTransformFeedback.Buffers[j].Stride =
                   prog->TransformFeedback.BufferStride[j] / 4;
             }
@@ -973,7 +1003,7 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,
          if (!tfeedback_decls[i].store(ctx, prog,
                                        &prog->LinkedTransformFeedback,
                                        num_buffers, num_outputs,
-                                       has_xfb_qualifiers))
+                                       explicit_stride, has_xfb_qualifiers))
             return false;
       }
    }
index 7165ecb918468e349629fdd18014050f784350b7..7919a8d5cd548a2b1e61df13ff21115bf90cae82 100644 (file)
@@ -98,7 +98,8 @@ public:
    unsigned get_num_outputs() const;
    bool store(struct gl_context *ctx, struct gl_shader_program *prog,
               struct gl_transform_feedback_info *info, unsigned buffer,
-              const unsigned max_outputs, bool has_xfb_qualifiers) const;
+              const unsigned max_outputs, bool *explicit_stride,
+              bool has_xfb_qualifiers) const;
    const tfeedback_candidate *find_candidate(gl_shader_program *prog,
                                              hash_table *tfeedback_candidates);