nir/tests: add serializer tests
[mesa.git] / src / compiler / glsl / gl_nir_link_xfb.c
index bcef1e1863da741ae8298661f8d0df65a77dbae2..5b80a3e0373d4d2703b8d926717077c27cb882fb 100644 (file)
  */
 
 #include "nir.h"
+#include "nir_xfb_info.h"
 #include "gl_nir_linker.h"
-#include "ir_uniform.h" /* for gl_uniform_storage */
 #include "linker_util.h"
 #include "main/context.h"
+#include "util/u_math.h"
 
 /*
  * This file does the linking of GLSL transform feedback using NIR.
  * particularities.
  */
 
-struct active_xfb_buffer {
-   GLuint stride;
-   GLuint num_varyings;
-};
-
-struct active_xfb_varyings {
-   unsigned num_varyings;
-   unsigned num_outputs;
-   unsigned buffer_size;
-   struct nir_variable **varyings;
-   struct active_xfb_buffer buffers[MAX_FEEDBACK_BUFFERS];
-};
-
-static unsigned
-get_num_outputs(nir_variable *var)
-{
-   return glsl_count_attribute_slots(var->type,
-                                     false /* is_vertex_input */);
-}
-
-static void
-add_xfb_varying(struct active_xfb_varyings *active_varyings,
-                nir_variable *var)
-{
-   if (active_varyings->num_varyings >= active_varyings->buffer_size) {
-      if (active_varyings->buffer_size == 0)
-         active_varyings->buffer_size = 1;
-      else
-         active_varyings->buffer_size *= 2;
-
-      active_varyings->varyings = realloc(active_varyings->varyings,
-                                          sizeof(nir_variable*) *
-                                          active_varyings->buffer_size);
-   }
-
-   active_varyings->varyings[active_varyings->num_varyings++] = var;
-
-   active_varyings->num_outputs += get_num_outputs(var);
-}
-
-static int
-cmp_xfb_offset(const void *x_generic, const void *y_generic)
-{
-   const nir_variable *const *x = x_generic;
-   const nir_variable *const *y = y_generic;
-
-   if ((*x)->data.xfb_buffer != (*y)->data.xfb_buffer)
-      return (*x)->data.xfb_buffer - (*y)->data.xfb_buffer;
-   return (*x)->data.offset - (*y)->data.offset;
-}
-
-static void
-get_active_xfb_varyings(struct gl_shader_program *prog,
-                        struct active_xfb_varyings *active_varyings)
-{
-   for (unsigned i = 0; i < MESA_SHADER_STAGES; ++i) {
-      struct gl_linked_shader *sh = prog->_LinkedShaders[i];
-      if (sh == NULL)
-         continue;
-
-      nir_shader *nir = sh->Program->nir;
-
-      nir_foreach_variable(var, &nir->outputs) {
-         if (var->data.explicit_xfb_buffer &&
-             var->data.explicit_xfb_stride) {
-            assert(var->data.xfb_buffer < MAX_FEEDBACK_BUFFERS);
-            active_varyings->buffers[var->data.xfb_buffer].stride =
-               var->data.xfb_stride;
-         }
-
-         if (!var->data.explicit_xfb_buffer ||
-             !var->data.explicit_offset)
-            continue;
-
-         active_varyings->buffers[var->data.xfb_buffer].num_varyings++;
-
-         add_xfb_varying(active_varyings, var);
-      }
-   }
-
-   /* The xfb_offset qualifier does not have to be used in increasing order
-    * however some drivers expect to receive the list of transform feedback
-    * declarations in order so sort it now for convenience.
-    */
-   qsort(active_varyings->varyings,
-         active_varyings->num_varyings,
-         sizeof(*active_varyings->varyings),
-         cmp_xfb_offset);
-}
-
-static unsigned
-add_varying_outputs(nir_variable *var,
-                    const struct glsl_type *type,
-                    unsigned location_offset,
-                    unsigned dest_offset,
-                    struct gl_transform_feedback_output *output)
-{
-   unsigned num_outputs = 0;
-
-   if (glsl_type_is_array(type) || glsl_type_is_matrix(type)) {
-      unsigned length = glsl_get_length(type);
-      const struct glsl_type *child_type = glsl_get_array_element(type);
-      unsigned component_slots = glsl_get_component_slots(child_type);
-
-      for (unsigned i = 0; i < length; i++) {
-         unsigned child_outputs = add_varying_outputs(var,
-                                                      child_type,
-                                                      location_offset,
-                                                      dest_offset,
-                                                      output + num_outputs);
-         num_outputs += child_outputs;
-         location_offset += child_outputs;
-         dest_offset += component_slots;
-      }
-   } else if (glsl_type_is_struct(type)) {
-      unsigned length = glsl_get_length(type);
-      for (unsigned i = 0; i < length; i++) {
-         const struct glsl_type *child_type = glsl_get_struct_field(type, i);
-         unsigned child_outputs = add_varying_outputs(var,
-                                                      child_type,
-                                                      location_offset,
-                                                      dest_offset,
-                                                      output + num_outputs);
-         num_outputs += child_outputs;
-         location_offset += child_outputs;
-         dest_offset += glsl_get_component_slots(child_type);
-      }
-   } else {
-      unsigned location = var->data.location + location_offset;
-      unsigned location_frac = var->data.location_frac;
-      unsigned num_components = glsl_get_component_slots(type);
-
-      while (num_components > 0) {
-         unsigned output_size = MIN2(num_components, 4 - location_frac);
-
-         output->OutputRegister = location;
-         output->OutputBuffer = var->data.xfb_buffer;
-         output->NumComponents = output_size;
-         output->StreamId = var->data.stream;
-         output->DstOffset = var->data.offset / 4 + dest_offset;
-         output->ComponentOffset = location_frac;
-
-         dest_offset += output_size;
-         num_components -= output_size;
-         num_outputs++;
-         output++;
-         location++;
-         location_frac = 0;
-      }
-   }
-
-   return num_outputs;
-}
-
 void
 gl_nir_link_assign_xfb_resources(struct gl_context *ctx,
                                  struct gl_shader_program *prog)
@@ -220,36 +67,57 @@ gl_nir_link_assign_xfb_resources(struct gl_context *ctx,
       free(prog->TransformFeedback.VaryingNames[i]);
    free(prog->TransformFeedback.VaryingNames);
 
-   struct active_xfb_varyings active_varyings = { 0 };
+   nir_xfb_info *xfb_info = NULL;
+   nir_xfb_varyings_info *varyings_info = NULL;
 
-   get_active_xfb_varyings(prog, &active_varyings);
+   /* Find last stage before fragment shader */
+   for (int stage = MESA_SHADER_FRAGMENT - 1; stage >= 0; stage--) {
+      struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
 
-   for (unsigned buf = 0; buf < MAX_FEEDBACK_BUFFERS; buf++)
-      prog->TransformFeedback.BufferStride[buf] = active_varyings.buffers[buf].stride;
-
-   prog->TransformFeedback.NumVarying = active_varyings.num_varyings;
-   prog->TransformFeedback.VaryingNames =
-      malloc(sizeof(GLchar *) * active_varyings.num_varyings);
+      if (sh && stage != MESA_SHADER_TESS_CTRL) {
+         xfb_info = nir_gather_xfb_info_with_varyings(sh->Program->nir, NULL, &varyings_info);
+         break;
+      }
+   }
 
    struct gl_transform_feedback_info *linked_xfb =
       rzalloc(xfb_prog, struct gl_transform_feedback_info);
    xfb_prog->sh.LinkedTransformFeedback = linked_xfb;
 
+   if (!xfb_info) {
+      prog->TransformFeedback.NumVarying = 0;
+      linked_xfb->NumOutputs = 0;
+      linked_xfb->NumVarying = 0;
+      linked_xfb->ActiveBuffers = 0;
+      return;
+   }
+
+   for (unsigned buf = 0; buf < MAX_FEEDBACK_BUFFERS; buf++)
+      prog->TransformFeedback.BufferStride[buf] = xfb_info->buffers[buf].stride;
+
+   prog->TransformFeedback.NumVarying = varyings_info->varying_count;
+   prog->TransformFeedback.VaryingNames =
+      malloc(sizeof(GLchar *) * varyings_info->varying_count);
+
    linked_xfb->Outputs =
       rzalloc_array(xfb_prog,
                     struct gl_transform_feedback_output,
-                    active_varyings.num_outputs);
-   linked_xfb->NumOutputs = active_varyings.num_outputs;
+                    xfb_info->output_count);
+   linked_xfb->NumOutputs = xfb_info->output_count;
 
    linked_xfb->Varyings =
       rzalloc_array(xfb_prog,
                     struct gl_transform_feedback_varying_info,
-                    active_varyings.num_varyings);
-   linked_xfb->NumVarying = active_varyings.num_varyings;
+                    varyings_info->varying_count);
+   linked_xfb->NumVarying = varyings_info->varying_count;
+
+   int buffer_index = 0; /* Corresponds to GL_TRANSFORM_FEEDBACK_BUFFER_INDEX */
+   int xfb_buffer =
+      (varyings_info->varying_count > 0) ?
+      xfb_info->outputs[0].buffer : 0;
 
-   struct gl_transform_feedback_output *output = linked_xfb->Outputs;
-   for (unsigned i = 0; i < active_varyings.num_varyings; i++) {
-      struct nir_variable *var = active_varyings.varyings[i];
+   for (unsigned i = 0; i < varyings_info->varying_count; i++) {
+      nir_xfb_varying_info *xfb_varying = &varyings_info->varyings[i];
 
       /* From ARB_gl_spirv spec:
        *
@@ -277,23 +145,35 @@ gl_nir_link_assign_xfb_resources(struct gl_context *ctx,
        */
       prog->TransformFeedback.VaryingNames[i] = NULL;
 
-      unsigned varying_outputs = add_varying_outputs(var,
-                                                     var->type,
-                                                     0, /* location_offset */
-                                                     0, /* dest_offset */
-                                                     output);
-      assert(varying_outputs == get_num_outputs(var));
-      output = output + varying_outputs;
+      if (xfb_buffer != xfb_varying->buffer) {
+         buffer_index++;
+         xfb_buffer = xfb_varying->buffer;
+      }
 
       struct gl_transform_feedback_varying_info *varying =
          linked_xfb->Varyings + i;
 
       /* ARB_gl_spirv: see above. */
       varying->Name = NULL;
-      varying->Type = glsl_get_gl_type(var->type);
-      varying->BufferIndex = var->data.xfb_buffer;
-      varying->Size = glsl_get_length(var->type);
-      varying->Offset = var->data.offset;
+      varying->Type = glsl_get_gl_type(xfb_varying->type);
+      varying->BufferIndex = buffer_index;
+      varying->Size = glsl_type_is_array(xfb_varying->type) ?
+         glsl_get_length(xfb_varying->type) : 1;
+      varying->Offset = xfb_varying->offset;
+   }
+
+   for (unsigned i = 0; i < xfb_info->output_count; i++) {
+      nir_xfb_output_info *xfb_output = &xfb_info->outputs[i];
+
+      struct gl_transform_feedback_output *output =
+         linked_xfb->Outputs + i;
+
+      output->OutputRegister = xfb_output->location;
+      output->OutputBuffer = xfb_output->buffer;
+      output->NumComponents = util_bitcount(xfb_output->component_mask);
+      output->StreamId = xfb_info->buffer_to_stream[xfb_output->buffer];
+      output->DstOffset = xfb_output->offset / 4;
+      output->ComponentOffset = xfb_output->component_offset;
    }
 
    /* Make sure MaxTransformFeedbackBuffers is <= 32 so the bitmask for
@@ -303,14 +183,14 @@ gl_nir_link_assign_xfb_resources(struct gl_context *ctx,
    assert(ctx->Const.MaxTransformFeedbackBuffers <= sizeof(buffers) * 8);
 
    for (unsigned buf = 0; buf < MAX_FEEDBACK_BUFFERS; buf++) {
-      if (active_varyings.buffers[buf].stride > 0) {
-         linked_xfb->Buffers[buf].Stride = active_varyings.buffers[buf].stride / 4;
-         linked_xfb->Buffers[buf].NumVaryings = active_varyings.buffers[buf].num_varyings;
+      if (xfb_info->buffers[buf].stride > 0) {
+         linked_xfb->Buffers[buf].Stride = xfb_info->buffers[buf].stride / 4;
+         linked_xfb->Buffers[buf].NumVaryings = xfb_info->buffers[buf].varying_count;
          buffers |= 1 << buf;
       }
    }
 
    linked_xfb->ActiveBuffers = buffers;
 
-   free(active_varyings.varyings);
+   ralloc_free(xfb_info);
 }