[Mesa-dev] [WIP 08/13] glsl/linker: GL_ARB_explicit_uniform_location support

Ian Romanick idr at freedesktop.org
Fri Apr 4 15:18:27 PDT 2014


On 03/27/2014 11:45 PM, Tapani Pälli wrote:
> Patch refactors the existing uniform processing so explicit locations
> are taken in to account during variable processing. These locations
> are temporarily stored in gl_uniform_storage before actual locations
> are set.
> 
> The 'remap_location' variable in gl_uniform_storage is changed to be
> signed so that we can use 0 as a valid explicit location and '-1' as
> identifier that no explicit location has been defined.
> 
> When locations are set, UniformRemapTable is first populated with
> uniforms that have explicit location set (inactive and actives ones),
> rest are put to any free locations that are left.
> 
> Signed-off-by: Tapani Pälli <tapani.palli at intel.com>
> ---
>  src/glsl/ir_uniform.h      |   5 +-
>  src/glsl/link_uniforms.cpp | 230 +++++++++++++++++++++++++++++++++++++++++----
>  2 files changed, 217 insertions(+), 18 deletions(-)
> 
> diff --git a/src/glsl/ir_uniform.h b/src/glsl/ir_uniform.h
> index 3508509..9dc4a8e 100644
> --- a/src/glsl/ir_uniform.h
> +++ b/src/glsl/ir_uniform.h
> @@ -181,9 +181,10 @@ struct gl_uniform_storage {
>  
>     /**
>      * The 'base location' for this uniform in the uniform remap table. For
> -    * arrays this is the first element in the array.
> +    * arrays this is the first element in the array. It needs to be signed
> +    * so that we can use 0 as valid location and -1 as initial value
>      */
> -   unsigned remap_location;
> +   int remap_location;
>  };
>  
>  #ifdef __cplusplus
> diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp
> index 9c959f6..885bdb5 100644
> --- a/src/glsl/link_uniforms.cpp
> +++ b/src/glsl/link_uniforms.cpp
> @@ -387,6 +387,9 @@ public:
>     void set_and_process(struct gl_shader_program *prog,
>  			ir_variable *var)
>     {
> +      current_var = var;
> +      field_counter = 0;
> +
>        ubo_block_index = -1;
>        if (var->is_in_uniform_block()) {
>           if (var->is_interface_instance() && var->type->is_array()) {
> @@ -543,6 +546,22 @@ private:
>           return;
>        }
>  
> +      /* Assign explicit locations. */
> +      if (current_var->data.explicit_location) {
> +         /* Set sequential locations for struct fields. */
> +         if (current_var->type->is_record()) {
> +            const unsigned entries = MAX2(1, this->uniforms[id].array_elements);
> +            this->uniforms[id].remap_location =
> +               current_var->data.location + field_counter;
> +               field_counter += entries;
> +         } else {
> +            this->uniforms[id].remap_location = current_var->data.location;
> +         }
> +      } else {
> +         /* Initialize to -1 to indicate that no explicit location is set */
> +         this->uniforms[id].remap_location = -1;
> +      }
> +
>        this->uniforms[id].name = ralloc_strdup(this->uniforms, name);
>        this->uniforms[id].type = base_type;
>        this->uniforms[id].initialized = 0;
> @@ -598,6 +617,17 @@ public:
>     gl_texture_index targets[MAX_SAMPLERS];
>  
>     /**
> +    * Current variable being processed.
> +    */
> +   ir_variable *current_var;
> +
> +   /**
> +    * Field counter is used to take care that uniform structures
> +    * with explicit locations get sequential locations.
> +    */
> +   unsigned field_counter;
> +
> +   /**
>      * Mask of samplers used by the current shader stage.
>      */
>     unsigned shader_samplers_used;
> @@ -723,6 +753,44 @@ link_update_uniform_buffer_variables(struct gl_shader *shader)
>     }
>  }
>  
> +/**
> + * Search for a large enough continuous space in the
> + * \c UniformRemapTable for a uniform.
> + */
> +static int
> +search_uniform_remap_slot(struct gl_shader_program *prog,
> +   gl_uniform_storage *uni)
> +{
> +   /* How many new entries required for this uniform? */
> +   const unsigned entries = MAX2(1, uni->array_elements);
> +
> +   /* Seek table if there is enough space available. */
> +   for (unsigned i = 0; i < prog->NumUniformRemapTable;) {
> +
> +      /* Location already reserved. */
> +      if (prog->UniformRemapTable[i]) {
> +         i++;
> +         continue;
> +      }
> +
> +      /* Not enough space for this uniform. */
> +      if (i + entries > prog->NumUniformRemapTable)
> +         return -1;
> +
> +      unsigned j;
> +      for (j = 0; j < entries; j++)
> +         if (prog->UniformRemapTable[i + j])
> +            break;
> +
> +      /* There was enough space. */
> +      if (j == entries)
> +         return i;
> +
> +      i += j;
> +   }
> +   return -1;
> +}
> +
>  void
>  link_assign_uniform_block_offsets(struct gl_shader *shader)
>  {
> @@ -792,6 +860,40 @@ link_set_image_access_qualifiers(struct gl_shader_program *prog)
>     }
>  }
>  
> +/**
> + * Fills the remap table entries for a gl_uniform_storage with
> + * explicit locations, fails if any of the locations required by
> + * uniform has been already reserved.
> + */
> +static bool
> +reserve_explicit_locations(struct gl_shader_program *prog,
> +                           struct gl_uniform_storage *storage)
> +{
> +   /* How many new entries for this uniform? */
> +   const unsigned entries = MAX2(1, storage->array_elements);
> +
> +   /* Set remap table entries point to correct gl_uniform_storage. */
> +   for (unsigned j = 0; j < entries; j++) {
> +      unsigned element_loc = storage->remap_location + j;
> +
> +      /* ARB_explicit_uniform_location specification states:
> +       *
> +       *     "No two default-block uniform variables in the program can have
> +       *     the same location, even if they are unused, otherwise a compiler
> +       *     or linker error will be generated."
> +       */
> +      if (prog->UniformRemapTable[element_loc]) {
> +         linker_error(prog, "location qualifier "
> +                      "for uniform %s "
> +                      "overlaps previously used location",
> +                      storage->name ? storage->name : "<inactive>");
> +         return false;
> +      }
> +      prog->UniformRemapTable[element_loc] = storage;
> +   }
> +   return true;
> +}
> +
>  bool
>  link_assign_uniform_locations(struct gl_context *ctx,
>                                struct gl_shader_program *prog)
> @@ -810,6 +912,9 @@ link_assign_uniform_locations(struct gl_context *ctx,
>        prog->UniformHash = new string_to_uint_map;
>     }
>  
> +   /* Maximum explicit location used by any of the uniforms. */
> +   unsigned max_exp_loc = 0;
> +
>     /* First pass: Count the uniform resources used by the user-defined
>      * uniforms.  While this happens, each active uniform will have an index
>      * assigned to it.
> @@ -856,6 +961,32 @@ link_assign_uniform_locations(struct gl_context *ctx,
>  	    continue;
>  	 }
>  
> +         if (var->data.explicit_location) {
> +
> +            /* Max location slot occupied by this variable. */
> +            max_exp_loc =
> +               MAX2(max_exp_loc,
> +                    var->data.location + var->type->component_slots() - 1);
> +
> +            /**
> +             * ARB_explicit_uniform_location specification states:
> +             *
> +             *     "The explicitly defined locations and the generated locations
> +             *     must be in the range of 0 to MAX_UNIFORM_LOCATIONS minus one."
> +             *
> +             *     "Valid locations for default-block uniform variable locations
> +             *     are in the range of 0 to the implementation-defined maximum
> +             *     number of uniform locations."
> +             */
> +            if (max_exp_loc >= ctx->Const.MaxUserAssignableUniformLocations) {
> +               linker_error(prog, "location qualifier for uniform %s "
> +                            ">= MAX_UNIFORM_LOCATIONS (%u)",
> +                            var->name,
> +                            ctx->Const.MaxUserAssignableUniformLocations);
> +               return false;
> +            }
> +         }
> +
>  	 uniform_size.process(var);
>        }
>  
> @@ -873,9 +1004,10 @@ link_assign_uniform_locations(struct gl_context *ctx,
>     const unsigned num_user_uniforms = uniform_size.num_active_uniforms;
>     const unsigned num_data_slots = uniform_size.num_values;
>  
> -   /* On the outside chance that there were no uniforms, bail out.
> +   /* On the outside chance that there were no uniforms which require
> +    * a location, bail out.
>      */
> -   if (num_user_uniforms == 0)
> +   if (num_user_uniforms == 0 && prog->NumReservedLocations == 0)
>        return true;
>  
>     struct gl_uniform_storage *uniforms =
> @@ -916,27 +1048,93 @@ link_assign_uniform_locations(struct gl_context *ctx,
>               sizeof(prog->_LinkedShaders[i]->SamplerTargets));
>     }
>  
> -   /* Build the uniform remap table that is used to set/get uniform locations */
> +
> +   /* First calculate remap table size based on the entries occupied
> +    * by each active uniform.
> +    */
> +   unsigned remap_entries = 0;
> +   for (unsigned i = 0; i < num_user_uniforms; i++)
> +      remap_entries += MAX2(1, uniforms[i].array_elements);
> +
> +
> +   /* Check if some of the removed uniforms had max biggest location,
> +    * i == location, i + 1 == component slots count
> +    */
> +   for (unsigned i = 0; i < prog->NumReservedLocations; i += 2)
> +      max_exp_loc = MAX2(max_exp_loc,
> +                         prog->ReservedUniformLocations[i] +
> +                         prog->ReservedUniformLocations[i + 1] - 1);
> +
> +   /* Final check for size of the remap table. Amount of active
> +    * uniforms vs maximum explicit location.
> +    */
> +   const unsigned remap_table_size = MAX2(remap_entries, max_exp_loc + 1);
> +
> +   prog->UniformRemapTable =
> +      rzalloc_array(prog, gl_uniform_storage *, remap_table_size);

Why not just allocation max_exp_loc + 1 + remap_entries?  See my comment
below about not being allowed to fail.  At the end we can rerealloc to
the actual size... or we could just keep reralloc'ing in the loop like
we did before.

> +
> +   prog->NumUniformRemapTable = remap_table_size;
> +
> +   /* Allocate a fake uniform storage which can be used to
> +    * distinguish if a remap table entry is one of the removed ones.
> +    */
> +   if (prog->NumReservedLocations) {
> +      prog->RemovedUniformStorage =
> +         rzalloc(prog->ReservedUniformLocations, gl_uniform_storage);
> +   }
> +
> +   /* Build the uniform remap table that is used to set/get uniform locations.
> +    * First reserve all the explicit locations of the inactive uniforms.
> +    */
> +   for (unsigned i = 0; i < prog->NumReservedLocations; i += 2) {
> +      /* Fill RemovedUniformStorage for this uniform. */
> +      prog->RemovedUniformStorage->remap_location =
> +         prog->ReservedUniformLocations[i];
> +      prog->RemovedUniformStorage->array_elements =
> +         prog->ReservedUniformLocations[i + 1];
> +
> +         if (!reserve_explicit_locations(prog, prog->RemovedUniformStorage))
> +            return false;
> +   }
> +
> +   /* Reserve all the explicit locations of the active uniforms. */
>     for (unsigned i = 0; i < num_user_uniforms; i++) {
> +      if (uniforms[i].remap_location != -1)
> +         if (!reserve_explicit_locations(prog, &uniforms[i]))
> +            return false;
> +   }
> +
> +   /* Reserve locations for rest of the uniforms. */
> +   for (unsigned i = 0; i < num_user_uniforms; i++) {
> +
> +      /* Explicit ones have been set already. */
> +      if (uniforms[i].remap_location != -1)
> +         continue;
>  
> -      /* how many new entries for this uniform? */
> +      /* 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);
> +      /* Seek the uniform remap table for a available slot. */
> +      int pos = search_uniform_remap_slot(prog, &uniforms[i]);
>  
> -      /* set pointers for this uniform */
> -      for (unsigned j = 0; j < entries; j++)
> -         prog->UniformRemapTable[prog->NumUniformRemapTable+j] = &uniforms[i];
> +      /* We cannot find big enough continuous slot for the uniform, this
> +       * can happen due to fragmentation in the available locations caused
> +       * by explicit locations.
> +       */
> +      if (pos == -1) {
> +         linker_error(prog, "cannot allocate space "
> +                      "for uniform %s ", uniforms[i].name);
> +         return false;
> +      }

There's nothing in the spec that allows us to fail in this situation.

>  
> -      /* set the base location in remap table for the uniform */
> -      uniforms[i].remap_location = prog->NumUniformRemapTable;
> +      /* Set pointers for the uniform. */
> +      for (unsigned j = pos; j < pos + entries; j++) {
> +         assert(prog->UniformRemapTable[j] == NULL);
> +         prog->UniformRemapTable[j] = &uniforms[i];
> +      }
>  
> -      prog->NumUniformRemapTable += entries;
> +      /* Set the base location in remap table for the uniform. */
> +      uniforms[i].remap_location = pos;
>     }
>  
>  #ifndef NDEBUG
> 



More information about the mesa-dev mailing list