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;
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;
/* 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. */
* 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) {
if (!prog->UniformRemapTable) {
linker_error(prog, "Out of memory during linking.\n");
- return false;
+ return -1;
}
/* Initialize allocated space. */
/* 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:
*
"location qualifier for uniform %s overlaps "
"previously used location\n",
var->name);
- return false;
+ return -1;
}
/* Initialize location as inactive before optimization
/* Note, base location used for arrays. */
map->put(var->data.location, var->name);
- return true;
+ return return_value;
}
static bool
* 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.
if (!uniform_map) {
linker_error(prog, "Out of memory during linking.\n");
- return;
+ return -1;
}
unsigned entries_total = 0;
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,
+ ¤t_block->link);
+ }
+
+ /* The current block continues, so we simply increment its slots */
+ current_block->slots++;
+ }
}
+
delete uniform_map;
+ return entries_total;
}
static bool
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
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)
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);
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,
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 */
#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
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.