diff --git a/src/compiler/glsl/link_uniforms.cpp b/src/compiler/glsl/link_uniforms.cpp index d18a2f2a1bc..deaba94df1c 100644 --- a/src/compiler/glsl/link_uniforms.cpp +++ b/src/compiler/glsl/link_uniforms.cpp @@ -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; + } - prog->NumUniformRemapTable += entries; + /* Verify that total amount of entries for explicit and implicit locations + * is less than MAX_UNIFORM_LOCATIONS. + */ + + 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. */ diff --git a/src/compiler/glsl/linker.cpp b/src/compiler/glsl/linker.cpp index bad1c1742b7..5326bfd4d68 100644 --- a/src/compiler/glsl/linker.cpp +++ b/src/compiler/glsl/linker.cpp @@ -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, + ¤t_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); diff --git a/src/compiler/glsl/linker.h b/src/compiler/glsl/linker.h index c80be1c7e22..a60bb6ed087 100644 --- a/src/compiler/glsl/linker.h +++ b/src/compiler/glsl/linker.h @@ -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 */ diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index e5f2da0b3c4..12d3863719b 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -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.