glsl: Fix handling of function calls inside nested loops.
[mesa.git] / src / glsl / link_uniforms.cpp
index 04218be7ac96911bbd65830c7816890f0a03a6cd..0a15739c2659f3581ae841648297aa2908be2fb3 100644 (file)
@@ -60,7 +60,7 @@ program_resource_visitor::process(const glsl_type *type, const char *name)
           || (type->is_array() && type->fields.array->is_interface()));
 
    char *name_copy = ralloc_strdup(NULL, name);
-   recursion(type, &name_copy, strlen(name), false);
+   recursion(type, &name_copy, strlen(name), false, NULL);
    ralloc_free(name_copy);
 }
 
@@ -75,26 +75,83 @@ program_resource_visitor::process(ir_variable *var)
     */
 
    /* Only strdup the name if we actually will need to modify it. */
-   if (t->is_record() || (t->is_array() && t->fields.array->is_record())) {
+   if (var->from_named_ifc_block_array) {
+      /* lower_named_interface_blocks created this variable by lowering an
+       * interface block array to an array variable.  For example if the
+       * original source code was:
+       *
+       *     out Blk { vec4 bar } foo[3];
+       *
+       * Then the variable is now:
+       *
+       *     out vec4 bar[3];
+       *
+       * We need to visit each array element using the names constructed like
+       * so:
+       *
+       *     Blk[0].bar
+       *     Blk[1].bar
+       *     Blk[2].bar
+       */
+      assert(t->is_array());
+      const glsl_type *ifc_type = var->get_interface_type();
+      char *name = ralloc_strdup(NULL, ifc_type->name);
+      size_t name_length = strlen(name);
+      for (unsigned i = 0; i < t->length; i++) {
+         size_t new_length = name_length;
+         ralloc_asprintf_rewrite_tail(&name, &new_length, "[%u].%s", i,
+                                      var->name);
+         /* Note: row_major is only meaningful for uniform blocks, and
+          * lowering is only applied to non-uniform interface blocks, so we
+          * can safely pass false for row_major.
+          */
+         recursion(var->type, &name, new_length, false, NULL);
+      }
+      ralloc_free(name);
+   } else if (var->from_named_ifc_block_nonarray) {
+      /* lower_named_interface_blocks created this variable by lowering a
+       * named interface block (non-array) to an ordinary variable.  For
+       * example if the original source code was:
+       *
+       *     out Blk { vec4 bar } foo;
+       *
+       * Then the variable is now:
+       *
+       *     out vec4 bar;
+       *
+       * We need to visit this variable using the name:
+       *
+       *     Blk.bar
+       */
+      const glsl_type *ifc_type = var->get_interface_type();
+      char *name = ralloc_asprintf(NULL, "%s.%s", ifc_type->name, var->name);
+      /* Note: row_major is only meaningful for uniform blocks, and lowering
+       * is only applied to non-uniform interface blocks, so we can safely
+       * pass false for row_major.
+       */
+      recursion(var->type, &name, strlen(name), false, NULL);
+      ralloc_free(name);
+   } else if (t->is_record() || (t->is_array() && t->fields.array->is_record())) {
       char *name = ralloc_strdup(NULL, var->name);
-      recursion(var->type, &name, strlen(name), false);
+      recursion(var->type, &name, strlen(name), false, NULL);
       ralloc_free(name);
    } else if (t->is_interface()) {
       char *name = ralloc_strdup(NULL, var->type->name);
-      recursion(var->type, &name, strlen(name), false);
+      recursion(var->type, &name, strlen(name), false, NULL);
       ralloc_free(name);
    } else if (t->is_array() && t->fields.array->is_interface()) {
       char *name = ralloc_strdup(NULL, var->type->fields.array->name);
-      recursion(var->type, &name, strlen(name), false);
+      recursion(var->type, &name, strlen(name), false, NULL);
       ralloc_free(name);
    } else {
-      this->visit_field(t, var->name, false);
+      this->visit_field(t, var->name, false, NULL);
    }
 }
 
 void
 program_resource_visitor::recursion(const glsl_type *t, char **name,
-                                    size_t name_length, bool row_major)
+                                    size_t name_length, bool row_major,
+                                    const glsl_type *record_type)
 {
    /* Records need to have each field processed individually.
     *
@@ -103,6 +160,9 @@ program_resource_visitor::recursion(const glsl_type *t, char **name,
     * individually.
     */
    if (t->is_record() || t->is_interface()) {
+      if (record_type == NULL && t->is_record())
+         record_type = t;
+
       for (unsigned i = 0; i < t->length; i++) {
         const char *field = t->fields.structure[i].name;
         size_t new_length = name_length;
@@ -118,24 +178,45 @@ program_resource_visitor::recursion(const glsl_type *t, char **name,
          }
 
          recursion(t->fields.structure[i].type, name, new_length,
-                   t->fields.structure[i].row_major);
+                   t->fields.structure[i].row_major, record_type);
+
+         /* Only the first leaf-field of the record gets called with the
+          * record type pointer.
+          */
+         record_type = NULL;
       }
    } else if (t->is_array() && (t->fields.array->is_record()
                                 || t->fields.array->is_interface())) {
+      if (record_type == NULL && t->fields.array->is_record())
+         record_type = t->fields.array;
+
       for (unsigned i = 0; i < t->length; i++) {
         size_t new_length = name_length;
 
         /* Append the subscript to the current variable name */
         ralloc_asprintf_rewrite_tail(name, &new_length, "[%u]", i);
 
-         recursion(t->fields.array, name, new_length,
-                   t->fields.structure[i].row_major);
+         recursion(t->fields.array, name, new_length, row_major,
+                   record_type);
+
+         /* Only the first leaf-field of the record gets called with the
+          * record type pointer.
+          */
+         record_type = NULL;
       }
    } else {
-      this->visit_field(t, *name, row_major);
+      this->visit_field(t, *name, row_major, record_type);
    }
 }
 
+void
+program_resource_visitor::visit_field(const glsl_type *type, const char *name,
+                                      bool row_major,
+                                      const glsl_type *record_type)
+{
+   visit_field(type, name, row_major);
+}
+
 void
 program_resource_visitor::visit_field(const glsl_struct_field *field)
 {
@@ -143,6 +224,8 @@ program_resource_visitor::visit_field(const glsl_struct_field *field)
    /* empty */
 }
 
+namespace {
+
 /**
  * Class to help calculate the storage requirements for a set of uniforms
  *
@@ -157,7 +240,7 @@ class count_uniform_size : public program_resource_visitor {
 public:
    count_uniform_size(struct string_to_uint_map *map)
       : num_active_uniforms(0), num_values(0), num_shader_samplers(0),
-       num_shader_uniform_components(0), map(map)
+       num_shader_uniform_components(0), is_ubo_var(false), map(map)
    {
       /* empty */
    }
@@ -170,9 +253,10 @@ public:
 
    void process(ir_variable *var)
    {
+      this->is_ubo_var = var->is_in_uniform_block();
       if (var->is_interface_instance())
-         program_resource_visitor::process(var->interface_type,
-                                           var->interface_type->name);
+         program_resource_visitor::process(var->get_interface_type(),
+                                           var->get_interface_type()->name);
       else
          program_resource_visitor::process(var);
    }
@@ -197,6 +281,8 @@ public:
     */
    unsigned num_shader_uniform_components;
 
+   bool is_ubo_var;
+
 private:
    virtual void visit_field(const glsl_type *type, const char *name,
                             bool row_major)
@@ -222,7 +308,8 @@ private:
          * Note that samplers do not count against this limit because they
          * don't use any storage on current hardware.
          */
-        this->num_shader_uniform_components += values;
+        if (!is_ubo_var)
+           this->num_shader_uniform_components += values;
       }
 
       /* If the uniform is already in the map, there's nothing more to do.
@@ -243,6 +330,8 @@ private:
    struct string_to_uint_map *map;
 };
 
+} /* anonymous namespace */
+
 /**
  * Class to help parcel out pieces of backing storage to uniforms
  *
@@ -284,10 +373,10 @@ public:
       ubo_block_index = -1;
       if (var->is_in_uniform_block()) {
          if (var->is_interface_instance() && var->type->is_array()) {
-            unsigned l = strlen(var->interface_type->name);
+            unsigned l = strlen(var->get_interface_type()->name);
 
             for (unsigned i = 0; i < prog->NumUniformBlocks; i++) {
-               if (strncmp(var->interface_type->name,
+               if (strncmp(var->get_interface_type()->name,
                            prog->UniformBlocks[i].Name,
                            l) == 0
                    && prog->UniformBlocks[i].Name[l] == '[') {
@@ -297,7 +386,7 @@ public:
             }
          } else {
             for (unsigned i = 0; i < prog->NumUniformBlocks; i++) {
-               if (strcmp(var->interface_type->name,
+               if (strcmp(var->get_interface_type()->name,
                           prog->UniformBlocks[i].Name) == 0) {
                   ubo_block_index = i;
                   break;
@@ -329,7 +418,8 @@ public:
          }
 
          if (var->is_interface_instance())
-            process(var->interface_type, var->interface_type->name);
+            process(var->get_interface_type(),
+                    var->get_interface_type()->name);
          else
             process(var);
       } else
@@ -372,6 +462,15 @@ private:
 
    virtual void visit_field(const glsl_type *type, const char *name,
                             bool row_major)
+   {
+      (void) type;
+      (void) name;
+      (void) row_major;
+      assert(!"Should not get here.");
+   }
+
+   virtual void visit_field(const glsl_type *type, const char *name,
+                            bool row_major, const glsl_type *record_type)
    {
       assert(!type->is_record());
       assert(!(type->is_array() && type->fields.array->is_record()));
@@ -414,10 +513,13 @@ private:
       this->uniforms[id].num_driver_storage = 0;
       this->uniforms[id].driver_storage = NULL;
       this->uniforms[id].storage = this->values;
+      this->uniforms[id].atomic_buffer_index = -1;
       if (this->ubo_block_index != -1) {
         this->uniforms[id].block_index = this->ubo_block_index;
 
-        unsigned alignment = type->std140_base_alignment(ubo_row_major);
+        const unsigned alignment = record_type
+           ? record_type->std140_base_alignment(ubo_row_major)
+           : type->std140_base_alignment(ubo_row_major);
         this->ubo_byte_offset = glsl_align(this->ubo_byte_offset, alignment);
         this->uniforms[id].offset = this->ubo_byte_offset;
         this->ubo_byte_offset += type->std140_size(ubo_row_major);
@@ -640,7 +742,9 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
     */
    count_uniform_size uniform_size(prog->UniformHash);
    for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
-      if (prog->_LinkedShaders[i] == NULL)
+      struct gl_shader *sh = prog->_LinkedShaders[i];
+
+      if (sh == NULL)
         continue;
 
       /* Uniforms that lack an initializer in the shader code have an initial
@@ -652,16 +756,15 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
        *     initializer, if present, or 0 if no initializer is present. Sampler
        *     types cannot have initializers."
        */
-      memset(prog->_LinkedShaders[i]->SamplerUnits, 0,
-             sizeof(gl_shader::SamplerUnits));
+      memset(sh->SamplerUnits, 0, sizeof(sh->SamplerUnits));
 
-      link_update_uniform_buffer_variables(prog->_LinkedShaders[i]);
+      link_update_uniform_buffer_variables(sh);
 
       /* Reset various per-shader target counts.
        */
       uniform_size.start_shader();
 
-      foreach_list(node, prog->_LinkedShaders[i]->ir) {
+      foreach_list(node, sh->ir) {
         ir_variable *const var = ((ir_instruction *) node)->as_variable();
 
         if ((var == NULL) || (var->mode != ir_var_uniform))
@@ -678,9 +781,14 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
         uniform_size.process(var);
       }
 
-      prog->_LinkedShaders[i]->num_samplers = uniform_size.num_shader_samplers;
-      prog->_LinkedShaders[i]->num_uniform_components =
-        uniform_size.num_shader_uniform_components;
+      sh->num_samplers = uniform_size.num_shader_samplers;
+      sh->num_uniform_components = uniform_size.num_shader_uniform_components;
+
+      sh->num_combined_uniform_components = sh->num_uniform_components;
+      for (unsigned i = 0; i < sh->NumUniformBlocks; i++) {
+        sh->num_combined_uniform_components +=
+           sh->UniformBlocks[i].UniformBufferSize / 4;
+      }
    }
 
    const unsigned num_user_uniforms = uniform_size.num_active_uniforms;
@@ -724,11 +832,25 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
       prog->_LinkedShaders[i]->active_samplers = parcel.shader_samplers_used;
       prog->_LinkedShaders[i]->shadow_samplers = parcel.shader_shadow_samplers;
 
-      assert(sizeof(gl_shader::SamplerTargets) == sizeof(parcel.targets));
+      STATIC_ASSERT(sizeof(prog->_LinkedShaders[i]->SamplerTargets) == sizeof(parcel.targets));
       memcpy(prog->_LinkedShaders[i]->SamplerTargets, parcel.targets,
-             sizeof(gl_shader::SamplerTargets));
+             sizeof(prog->_LinkedShaders[i]->SamplerTargets));
    }
 
+   /* Determine the size of the largest uniform array queryable via
+    * glGetUniformLocation.  Using this as the location scale guarantees that
+    * there is enough "room" for the array index to be stored in the low order
+    * part of the uniform location.  It also makes the locations be more
+    * tightly packed.
+    */
+   unsigned max_array_size = 1;
+   for (unsigned i = 0; i < num_user_uniforms; i++) {
+      if (uniforms[i].array_elements > max_array_size)
+         max_array_size = uniforms[i].array_elements;
+   }
+
+   prog->UniformLocationBaseScale = max_array_size;
+
 #ifndef NDEBUG
    for (unsigned i = 0; i < num_user_uniforms; i++) {
       assert(uniforms[i].storage != NULL);