diff --git a/src/glsl/ir_uniform.h b/src/glsl/ir_uniform.h index 7508f795d08..3508509d4d0 100644 --- a/src/glsl/ir_uniform.h +++ b/src/glsl/ir_uniform.h @@ -178,6 +178,12 @@ struct gl_uniform_storage { * an atomic counter. */ int atomic_buffer_index; + + /** + * The 'base location' for this uniform in the uniform remap table. For + * arrays this is the first element in the array. + */ + unsigned remap_location; }; #ifdef __cplusplus diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp index 1c451e7f51c..3fedf313cbd 100644 --- a/src/glsl/link_uniforms.cpp +++ b/src/glsl/link_uniforms.cpp @@ -799,6 +799,10 @@ link_assign_uniform_locations(struct gl_shader_program *prog) prog->UniformStorage = NULL; prog->NumUserUniformStorage = 0; + ralloc_free(prog->UniformRemapTable); + prog->UniformRemapTable = NULL; + prog->NumUniformRemapTable = 0; + if (prog->UniformHash != NULL) { prog->UniformHash->clear(); } else { @@ -911,19 +915,28 @@ link_assign_uniform_locations(struct gl_shader_program *prog) 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; + /* Build the uniform remap table that is used to set/get uniform locations */ 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; + /* 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); + + /* set pointers for this uniform */ + for (unsigned j = 0; j < entries; j++) + prog->UniformRemapTable[prog->NumUniformRemapTable+j] = &uniforms[i]; + + /* set the base location in remap table for the uniform */ + uniforms[i].remap_location = prog->NumUniformRemapTable; + + prog->NumUniformRemapTable += entries; + } #ifndef NDEBUG for (unsigned i = 0; i < num_user_uniforms; i++) { diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index e5f10baab10..7c83d664f60 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -2701,6 +2701,14 @@ struct gl_shader_program unsigned NumUserUniformStorage; struct gl_uniform_storage *UniformStorage; + /** + * Mapping from GL uniform locations returned by \c glUniformLocation to + * UniformStorage entries. Arrays will have multiple contiguous slots + * in the UniformRemapTable, all pointing to the same UniformStorage entry. + */ + unsigned NumUniformRemapTable; + struct gl_uniform_storage **UniformRemapTable; + /** * Size of the gl_ClipDistance array that is output from the last pipeline * stage before the fragment shader. @@ -2710,21 +2718,6 @@ struct gl_shader_program struct gl_uniform_block *UniformBlocks; unsigned NumUniformBlocks; - /** - * Scale factor for the uniform base location - * - * This is used to generate locations (returned by \c glGetUniformLocation) - * of uniforms. The base location of the uniform is multiplied by this - * value, and the array index is added. - * - * \note - * Must be >= 1. - * - * \sa - * _mesa_uniform_merge_location_offset, _mesa_uniform_split_location_offset - */ - unsigned UniformLocationBaseScale; - /** * Indices into the _LinkedShaders's UniformBlocks[] array for each stage * they're used in, or -1. diff --git a/src/mesa/main/shaderobj.c b/src/mesa/main/shaderobj.c index d5c3d8099a7..b0f0bfa915b 100644 --- a/src/mesa/main/shaderobj.c +++ b/src/mesa/main/shaderobj.c @@ -285,7 +285,12 @@ _mesa_clear_shader_program_data(struct gl_context *ctx, ralloc_free(shProg->UniformStorage); shProg->NumUserUniformStorage = 0; shProg->UniformStorage = NULL; - shProg->UniformLocationBaseScale = 0; + } + + if (shProg->UniformRemapTable) { + ralloc_free(shProg->UniformRemapTable); + shProg->NumUniformRemapTable = 0; + shProg->UniformRemapTable = NULL; } if (shProg->UniformHash) { diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp index 8cc5da752ac..20ffbe8494c 100644 --- a/src/mesa/main/uniform_query.cpp +++ b/src/mesa/main/uniform_query.cpp @@ -246,14 +246,15 @@ validate_uniform_parameters(struct gl_context *ctx, return false; } - _mesa_uniform_split_location_offset(shProg, location, loc, array_index); - - if (*loc >= shProg->NumUserUniformStorage) { + /* Check that the given location is in bounds of uniform remap table. */ + if (location >= (GLint) shProg->NumUniformRemapTable) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)", caller, location); return false; } + _mesa_uniform_split_location_offset(shProg, location, loc, array_index); + if (shProg->UniformStorage[*loc].array_elements == 0 && count > 1) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(count > 1 for non-array, location=%d)", diff --git a/src/mesa/main/uniforms.h b/src/mesa/main/uniforms.h index bd50fd9b84d..4a5a1aa1513 100644 --- a/src/mesa/main/uniforms.h +++ b/src/mesa/main/uniforms.h @@ -340,39 +340,46 @@ struct gl_builtin_uniform_desc { * element. We could insert dummy entries in the list for each array * element after [0] but that causes complications elsewhere. * - * We solve this problem by encoding two values in the location that's - * returned by glGetUniformLocation(): - * a) index into gl_uniform_list::Uniforms[] for the uniform - * b) an array/field offset (0 for simple types) + * We solve this problem by creating multiple entries for uniform arrays + * in the UniformRemapTable so that their elements get sequential locations. + * + * Utility functions below offer functionality to split UniformRemapTable + * location in to location of the uniform in UniformStorage + offset to the + * array element (0 if not an array) and also merge it back again as the + * UniformRemapTable location. * - * These two values are encoded in the high and low halves of a GLint. - * By putting the uniform number in the high part and the offset in the - * low part, we can support the unofficial ability to index into arrays - * by adding offsets to the location value. */ /*@{*/ /** - * Combine the uniform's base location and the offset + * Combine the uniform's storage index and the array index */ static inline GLint _mesa_uniform_merge_location_offset(const struct gl_shader_program *prog, - unsigned base_location, unsigned offset) + unsigned storage_index, + unsigned uniform_array_index) { - assert(prog->UniformLocationBaseScale >= 1); - assert(offset < prog->UniformLocationBaseScale); - return (base_location * prog->UniformLocationBaseScale) + offset; + /* location in remap table + array element offset */ + return prog->UniformStorage[storage_index].remap_location + + uniform_array_index; } /** - * Separate the uniform base location and parameter offset + * Separate the uniform storage index and array index */ static inline void _mesa_uniform_split_location_offset(const struct gl_shader_program *prog, - GLint location, unsigned *base_location, - unsigned *offset) + GLint location, unsigned *storage_index, + unsigned *uniform_array_index) { - *offset = location % prog->UniformLocationBaseScale; - *base_location = location / prog->UniformLocationBaseScale; + *storage_index = prog->UniformRemapTable[location] - prog->UniformStorage; + *uniform_array_index = location - + prog->UniformRemapTable[location]->remap_location; + + /*gl_uniform_storage in UniformStorage with the calculated base_location + * must match with the entry in remap table + */ + assert(&prog->UniformStorage[*storage_index] == + prog->UniformRemapTable[location]); } /*@}*/