glsl: validate global out xfb_stride qualifiers and set stride on empty buffers
authorTimothy Arceri <timothy.arceri@collabora.com>
Fri, 11 Mar 2016 00:57:52 +0000 (11:57 +1100)
committerTimothy Arceri <timothy.arceri@collabora.com>
Thu, 31 Mar 2016 01:52:00 +0000 (12:52 +1100)
Here we use the built-in validation in
ast_layout_expression::process_qualifier_constant() to check for mismatching
global out strides on buffers in a single shader.

From the ARB_enhanced_layouts spec:

   "While *xfb_stride* can be declared multiple times for the same buffer,
   it is a compile-time or link-time error to have different values
   specified for the stride for the same buffer."

For intrastage validation a new helper link_xfb_stride_layout_qualifiers()
is created. We also take this opportunity to make sure stride is at least
a multiple of 4, we will validate doubles at a later stage.

From the ARB_enhanced_layouts spec:

   "If the buffer is capturing any double-typed outputs, the stride must
   be a multiple of 8, otherwise it must be a multiple of 4, or a
   compile-time or link-time error results."

Finally we update store_tfeedback_info() to apply the strides to
LinkedTransformFeedback and update the buffers bitmask to mark any global
buffers with a stride as active. For example a shader with:

layout (xfb_buffer = 0, xfb_offset = 0)  out vec4 gs_fs;
layout (xfb_buffer = 1, xfb_stride = 64) out;

Is expected to have a buffer bound to both 0 and 1.

From the ARB_enhanced_layouts spec:

   "A binding point requires a bound buffer object if and only if its
   associated stride in the program object used for transform feedback
   primitive capture is non-zero."

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

index b88b62205137e22bfc8b2c65329769508f6fe401..0ce89ceb3a843fdf3a8f31282eed78026ec4c769 100644 (file)
@@ -1617,6 +1617,17 @@ set_shader_inout_layout(struct gl_shader *shader,
       assert(!state->fs_early_fragment_tests);
    }
 
+   for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) {
+      if (state->out_qualifier->out_xfb_stride[i]) {
+         unsigned xfb_stride;
+         if (state->out_qualifier->out_xfb_stride[i]->
+                process_qualifier_constant(state, "xfb_stride", &xfb_stride,
+                true)) {
+            shader->TransformFeedback.BufferStride[i] = xfb_stride;
+         }
+      }
+   }
+
    switch (shader->Stage) {
    case MESA_SHADER_TESS_CTRL:
       shader->TessCtrl.VerticesOut = 0;
index 5645f783f3fc74ac1c004282dc41f9207fb9f96f..d91642dea5ce39ac415daf06f63beeea9d0488c9 100644 (file)
@@ -930,6 +930,17 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,
       unsigned buffer =
          num_tfeedback_decls ? tfeedback_decls[0].get_buffer() : 0;
 
+      /* 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;
+               prog->LinkedTransformFeedback.Buffers[j].Stride =
+                  prog->TransformFeedback.BufferStride[j] / 4;
+            }
+         }
+      }
+
       for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
          if (tfeedback_decls[i].is_next_buffer_separator()) {
             num_buffers++;
index 3ae958cacb7f684261593a25296b74a0126be138..4f191c5002db86ee8d19852d6c9c9fa5bdedcb11 100644 (file)
@@ -1584,6 +1584,69 @@ private:
    hash_table *unnamed_interfaces;
 };
 
+/**
+ * Check for conflicting xfb_stride default qualifiers and store buffer stride
+ * for later use.
+ */
+static void
+link_xfb_stride_layout_qualifiers(struct gl_context *ctx,
+                                  struct gl_shader_program *prog,
+                                 struct gl_shader *linked_shader,
+                                 struct gl_shader **shader_list,
+                                 unsigned num_shaders)
+{
+   for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) {
+      linked_shader->TransformFeedback.BufferStride[i] = 0;
+   }
+
+   for (unsigned i = 0; i < num_shaders; i++) {
+      struct gl_shader *shader = shader_list[i];
+
+      for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
+         if (shader->TransformFeedback.BufferStride[j]) {
+           if (linked_shader->TransformFeedback.BufferStride[j] != 0 &&
+                shader->TransformFeedback.BufferStride[j] != 0 &&
+               linked_shader->TransformFeedback.BufferStride[j] !=
+                   shader->TransformFeedback.BufferStride[j]) {
+              linker_error(prog,
+                            "intrastage shaders defined with conflicting "
+                            "xfb_stride for buffer %d (%d and %d)\n", j,
+                            linked_shader->TransformFeedback.BufferStride[j],
+                           shader->TransformFeedback.BufferStride[j]);
+              return;
+           }
+
+            if (shader->TransformFeedback.BufferStride[j])
+              linked_shader->TransformFeedback.BufferStride[j] =
+                  shader->TransformFeedback.BufferStride[j];
+         }
+      }
+   }
+
+   for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
+      if (linked_shader->TransformFeedback.BufferStride[j]) {
+         prog->TransformFeedback.BufferStride[j] =
+            linked_shader->TransformFeedback.BufferStride[j];
+
+         /* We will validate doubles at a later stage */
+         if (prog->TransformFeedback.BufferStride[j] % 4) {
+            linker_error(prog, "invalid qualifier xfb_stride=%d must be a "
+                         "multiple of 4 or if its applied to a type that is "
+                         "or contains a double a multiple of 8.",
+                         prog->TransformFeedback.BufferStride[j]);
+            return;
+         }
+
+         if (prog->TransformFeedback.BufferStride[j] / 4 >
+             ctx->Const.MaxTransformFeedbackInterleavedComponents) {
+            linker_error(prog,
+                         "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
+                         "limit has been exceeded.");
+                  return;
+         }
+      }
+   }
+}
 
 /**
  * Performs the cross-validation of tessellation control shader vertices and
@@ -2101,6 +2164,8 @@ link_intrastage_shaders(void *mem_ctx,
    link_tes_in_layout_qualifiers(prog, linked, shader_list, num_shaders);
    link_gs_inout_layout_qualifiers(prog, linked, shader_list, num_shaders);
    link_cs_input_layout_qualifiers(prog, linked, shader_list, num_shaders);
+   link_xfb_stride_layout_qualifiers(ctx, prog, linked, shader_list,
+                                     num_shaders);
 
    populate_symbol_table(linked);
 
index 90c3851e72a91c5e6f5d851cea5ca2aeee59db1b..1e15b69ab7055c54cbf21dab29f53b3ac75cee06 100644 (file)
@@ -2337,6 +2337,11 @@ struct gl_shader
    bool origin_upper_left;
    bool pixel_center_integer;
 
+   struct {
+      /** Global xfb_stride out qualifier if any */
+      GLuint BufferStride[MAX_FEEDBACK_BUFFERS];
+   } TransformFeedback;
+
    /**
     * Tessellation Control shader state from layout qualifiers.
     */
@@ -2674,6 +2679,8 @@ struct gl_shader_program
     */
    struct {
       GLenum BufferMode;
+      /** Global xfb_stride out qualifier if any */
+      GLuint BufferStride[MAX_FEEDBACK_BUFFERS];
       GLuint NumVarying;
       GLchar **VaryingNames;  /**< Array [NumVarying] of char * */
    } TransformFeedback;