compiler/glsl: Fix uniform location counting.
authorPlamena Manolova <plamena.manolova@intel.com>
Thu, 11 Feb 2016 13:00:02 +0000 (15:00 +0200)
committerTapani Pälli <tapani.palli@intel.com>
Thu, 18 Feb 2016 09:53:35 +0000 (11:53 +0200)
This patch moves the calculation of current uniforms to
link_uniforms, which makes use of UniformRemapTable which
stores all the reserved uniform locations.

Location assignment for implicit uniforms now tries to use
any gaps left in the table after the location assignment
for explicit uniforms. This gives us more space to store more
uniforms.

Patch is based on earlier patch with following changes/additions:

   1: Move the counting of explicit locations to
      check_explicit_uniform_locations and then pass
      the number to link_assign_uniform_locations.
   2: Count the number of empty slots in UniformRemapTable
      and store them in a list_head.
   3: Try to find an empty slot for implicit locations from
      the list, if that fails resize UniformRemapTable.

Fixes following CTS tests:
   ES31-CTS.explicit_uniform_location.uniform-loc-mix-with-implicit-max
   ES31-CTS.explicit_uniform_location.uniform-loc-mix-with-implicit-max-array

Signed-off-by: Tapani Pälli <tapani.palli@intel.com>
Signed-off-by: Plamena Manolova <plamena.manolova@intel.com>
Reviewed-by: Ilia Mirkin <imirkin@alum.mit.edu>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=93696

src/compiler/glsl/link_uniforms.cpp
src/compiler/glsl/linker.cpp
src/compiler/glsl/linker.h
src/mesa/main/mtypes.h

index d18a2f2a1bc4dd879f74f53c641a417a2df92579..deaba94df1cc5298638353552329b671cf1317b2 100644 (file)
@@ -1038,9 +1038,43 @@ assign_hidden_uniform_slot_id(const char *name, unsigned hidden_id,
    uniform_size->map->put(hidden_uniform_start + hidden_id, name);
 }
 
+/**
+ * Search through the list of empty blocks to find one that fits the current
+ * uniform.
+ */
+static int
+find_empty_block(struct gl_shader_program *prog,
+                 struct gl_uniform_storage *uniform)
+{
+   const unsigned entries = MAX2(1, uniform->array_elements);
+
+   foreach_list_typed(struct empty_uniform_block, block, link,
+                      &prog->EmptyUniformLocations) {
+      /* Found a block with enough slots to fit the uniform */
+      if (block->slots == entries) {
+         unsigned start = block->start;
+         exec_node_remove(&block->link);
+         ralloc_free(block);
+
+         return start;
+      /* Found a block with more slots than needed. It can still be used. */
+      } else if (block->slots > entries) {
+         unsigned start = block->start;
+         block->start += entries;
+         block->slots -= entries;
+
+         return start;
+      }
+   }
+
+   return -1;
+}
+
 void
 link_assign_uniform_locations(struct gl_shader_program *prog,
-                              unsigned int boolean_true)
+                              unsigned int boolean_true,
+                              unsigned int num_explicit_uniform_locs,
+                              unsigned int max_uniform_locs)
 {
    ralloc_free(prog->UniformStorage);
    prog->UniformStorage = NULL;
@@ -1131,6 +1165,9 @@ link_assign_uniform_locations(struct gl_shader_program *prog,
 
    parcel_out_uniform_storage parcel(prog, prog->UniformHash, uniforms, data);
 
+   unsigned total_entries = num_explicit_uniform_locs;
+   unsigned empty_locs = prog->NumUniformRemapTable - num_explicit_uniform_locs;
+
    for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
       if (prog->_LinkedShaders[i] == NULL)
         continue;
@@ -1194,21 +1231,44 @@ link_assign_uniform_locations(struct gl_shader_program *prog,
       /* how many new entries for this uniform? */
       const unsigned entries = MAX2(1, uniforms[i].array_elements);
 
-      /* resize remap table to fit new entries */
-      prog->UniformRemapTable =
-         reralloc(prog,
-                  prog->UniformRemapTable,
-                  gl_uniform_storage *,
-                  prog->NumUniformRemapTable + entries);
+      /* Find UniformRemapTable for empty blocks where we can fit this uniform. */
+      int chosen_location = -1;
+
+      if (empty_locs)
+         chosen_location = find_empty_block(prog, &uniforms[i]);
+
+      /* Add new entries to the total amount of entries. */
+      total_entries += entries;
+
+      if (chosen_location != -1) {
+         empty_locs -= entries;
+      } else {
+         chosen_location = prog->NumUniformRemapTable;
+
+         /* resize remap table to fit new entries */
+         prog->UniformRemapTable =
+            reralloc(prog,
+                     prog->UniformRemapTable,
+                     gl_uniform_storage *,
+                     prog->NumUniformRemapTable + entries);
+         prog->NumUniformRemapTable += entries;
+      }
 
       /* set pointers for this uniform */
       for (unsigned j = 0; j < entries; j++)
-         prog->UniformRemapTable[prog->NumUniformRemapTable+j] = &uniforms[i];
+         prog->UniformRemapTable[chosen_location + j] = &uniforms[i];
 
       /* set the base location in remap table for the uniform */
-      uniforms[i].remap_location = prog->NumUniformRemapTable;
+      uniforms[i].remap_location = chosen_location;
+   }
+
+   /* Verify that total amount of entries for explicit and implicit locations
+    * is less than MAX_UNIFORM_LOCATIONS.
+    */
 
-      prog->NumUniformRemapTable += entries;
+   if (total_entries > max_uniform_locs) {
+      linker_error(prog, "count of uniform locations > MAX_UNIFORM_LOCATIONS"
+                   "(%u > %u)", total_entries, max_uniform_locs);
    }
 
    /* Reserve all the explicit locations of the active subroutine uniforms. */
index bad1c1742b7e68a37757d75bab4bcdc1d759b245..5326bfd4d68bed1c318871ce21786506c63883ca 100644 (file)
@@ -3008,12 +3008,13 @@ check_image_resources(struct gl_context *ctx, struct gl_shader_program *prog)
  * for a variable, checks for overlaps between other uniforms using explicit
  * locations.
  */
-static bool
+static int
 reserve_explicit_locations(struct gl_shader_program *prog,
                            string_to_uint_map *map, ir_variable *var)
 {
    unsigned slots = var->type->uniform_locations();
    unsigned max_loc = var->data.location + slots - 1;
+   unsigned return_value = slots;
 
    /* Resize remap table if locations do not fit in the current one. */
    if (max_loc + 1 > prog->NumUniformRemapTable) {
@@ -3024,7 +3025,7 @@ reserve_explicit_locations(struct gl_shader_program *prog,
 
       if (!prog->UniformRemapTable) {
          linker_error(prog, "Out of memory during linking.\n");
-         return false;
+         return -1;
       }
 
       /* Initialize allocated space. */
@@ -3042,8 +3043,10 @@ reserve_explicit_locations(struct gl_shader_program *prog,
 
          /* Possibly same uniform from a different stage, this is ok. */
          unsigned hash_loc;
-         if (map->get(hash_loc, var->name) && hash_loc == loc - i)
-               continue;
+         if (map->get(hash_loc, var->name) && hash_loc == loc - i) {
+            return_value = 0;
+            continue;
+         }
 
          /* ARB_explicit_uniform_location specification states:
           *
@@ -3055,7 +3058,7 @@ reserve_explicit_locations(struct gl_shader_program *prog,
                       "location qualifier for uniform %s overlaps "
                       "previously used location\n",
                       var->name);
-         return false;
+         return -1;
       }
 
       /* Initialize location as inactive before optimization
@@ -3067,7 +3070,7 @@ reserve_explicit_locations(struct gl_shader_program *prog,
    /* Note, base location used for arrays. */
    map->put(var->data.location, var->name);
 
-   return true;
+   return return_value;
 }
 
 static bool
@@ -3128,12 +3131,12 @@ reserve_subroutine_explicit_locations(struct gl_shader_program *prog,
  * any optimizations happen to handle also inactive uniforms and
  * inactive array elements that may get trimmed away.
  */
-static void
+static int
 check_explicit_uniform_locations(struct gl_context *ctx,
                                  struct gl_shader_program *prog)
 {
    if (!ctx->Extensions.ARB_explicit_uniform_location)
-      return;
+      return -1;
 
    /* This map is used to detect if overlapping explicit locations
     * occur with the same uniform (from different stage) or a different one.
@@ -3142,7 +3145,7 @@ check_explicit_uniform_locations(struct gl_context *ctx,
 
    if (!uniform_map) {
       linker_error(prog, "Out of memory during linking.\n");
-      return;
+      return -1;
    }
 
    unsigned entries_total = 0;
@@ -3157,31 +3160,47 @@ check_explicit_uniform_locations(struct gl_context *ctx,
          if (!var || var->data.mode != ir_var_uniform)
             continue;
 
-         entries_total += var->type->uniform_locations();
-
          if (var->data.explicit_location) {
-            bool ret;
+            bool ret = false;
             if (var->type->without_array()->is_subroutine())
                ret = reserve_subroutine_explicit_locations(prog, sh, var);
-            else
-               ret = reserve_explicit_locations(prog, uniform_map, var);
+            else {
+               int slots = reserve_explicit_locations(prog, uniform_map,
+                                                      var);
+               if (slots != -1) {
+                  ret = true;
+                  entries_total += slots;
+               }
+            }
             if (!ret) {
                delete uniform_map;
-               return;
+               return -1;
             }
          }
       }
    }
 
-   /* Verify that total amount of entries for explicit and implicit locations
-    * is less than MAX_UNIFORM_LOCATIONS.
-    */
-   if (entries_total >= ctx->Const.MaxUserAssignableUniformLocations) {
-      linker_error(prog, "count of uniform locations >= MAX_UNIFORM_LOCATIONS"
-                   "(%u >= %u)", entries_total,
-                   ctx->Const.MaxUserAssignableUniformLocations);
+   exec_list_make_empty(&prog->EmptyUniformLocations);
+   struct empty_uniform_block *current_block = NULL;
+
+   for (unsigned i = 0; i < prog->NumUniformRemapTable; i++) {
+      /* We found empty space in UniformRemapTable. */
+      if (prog->UniformRemapTable[i] == NULL) {
+         /* We've found the beginning of a new continous block of empty slots */
+         if (!current_block || current_block->start + current_block->slots != i) {
+            current_block = rzalloc(prog, struct empty_uniform_block);
+            current_block->start = i;
+            exec_list_push_tail(&prog->EmptyUniformLocations,
+                                &current_block->link);
+         }
+
+         /* The current block continues, so we simply increment its slots */
+         current_block->slots++;
+      }
    }
+
    delete uniform_map;
+   return entries_total;
 }
 
 static bool
@@ -4129,6 +4148,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 
    tfeedback_decl *tfeedback_decls = NULL;
    unsigned num_tfeedback_decls = prog->TransformFeedback.NumVarying;
+   unsigned int num_explicit_uniform_locs = 0;
 
    void *mem_ctx = ralloc_context(NULL); // temporary linker context
 
@@ -4310,7 +4330,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
       last = i;
    }
 
-   check_explicit_uniform_locations(ctx, prog);
+   num_explicit_uniform_locs = check_explicit_uniform_locations(ctx, prog);
    link_assign_subroutine_types(prog);
 
    if (!prog->LinkStatus)
@@ -4541,7 +4561,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
       goto done;
 
    update_array_sizes(prog);
-   link_assign_uniform_locations(prog, ctx->Const.UniformBooleanTrue);
+   link_assign_uniform_locations(prog, ctx->Const.UniformBooleanTrue,
+                                 num_explicit_uniform_locs,
+                                 ctx->Const.MaxUserAssignableUniformLocations);
    link_assign_atomic_counter_resources(ctx, prog);
    store_fragdepth_layout(prog);
 
index c80be1c7e226df6671aee507b609ce8be5cee110..a60bb6ed087fc2f87129e0bb58536238d1876036 100644 (file)
@@ -35,7 +35,9 @@ link_invalidate_variable_locations(exec_list *ir);
 
 extern void
 link_assign_uniform_locations(struct gl_shader_program *prog,
-                              unsigned int boolean_true);
+                              unsigned int boolean_true,
+                              unsigned int num_explicit_uniform_locs,
+                              unsigned int max_uniform_locs);
 
 extern void
 link_set_uniform_initializers(struct gl_shader_program *prog,
@@ -202,4 +204,17 @@ linker_error(gl_shader_program *prog, const char *fmt, ...);
 void
 linker_warning(gl_shader_program *prog, const char *fmt, ...);
 
+/**
+ * Sometimes there are empty slots left over in UniformRemapTable after we
+ * allocate slots to explicit locations. This struct represents a single
+ * continouous block of empty slots in UniformRemapTable.
+ */
+struct empty_uniform_block {
+   struct exec_node link;
+   /* The start location of the block */
+   unsigned start;
+   /* The number of slots in the block */
+   unsigned slots;
+};
+
 #endif /* GLSL_LINKER_H */
index e5f2da0b3c4bf5611a173f642733159dc91c9a78..12d3863719b95228fa0f7718e3fad8eb8860e9bd 100644 (file)
@@ -44,6 +44,7 @@
 #include "math/m_matrix.h"     /* GLmatrix */
 #include "compiler/shader_enums.h"
 #include "main/formats.h"       /* MESA_FORMAT_COUNT */
+#include "compiler/glsl/list.h"
 
 
 #ifdef __cplusplus
@@ -2770,6 +2771,13 @@ struct gl_shader_program
    unsigned NumUniformRemapTable;
    struct gl_uniform_storage **UniformRemapTable;
 
+   /**
+    * Sometimes there are empty slots left over in UniformRemapTable after we
+    * allocate slots to explicit locations. This list stores the blocks of
+    * continuous empty slots inside UniformRemapTable.
+    */
+   struct exec_list EmptyUniformLocations;
+
    /**
     * Size of the gl_ClipDistance array that is output from the last pipeline
     * stage before the fragment shader.