X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fcompiler%2Fglsl%2Flink_uniform_blocks.cpp;h=45f1c0fe98d7e7b457c13b14e9141a2ec674298c;hb=35108caa71d4da02303335ee52a0833fe650d85a;hp=7d75576585226b300876ec088a196dab50612b76;hpb=eb63640c1d38a200a7b1540405051d3ff79d0d8a;p=mesa.git diff --git a/src/compiler/glsl/link_uniform_blocks.cpp b/src/compiler/glsl/link_uniform_blocks.cpp index 7d755765852..45f1c0fe98d 100644 --- a/src/compiler/glsl/link_uniform_blocks.cpp +++ b/src/compiler/glsl/link_uniform_blocks.cpp @@ -21,22 +21,26 @@ * DEALINGS IN THE SOFTWARE. */ -#include "main/core.h" #include "ir.h" #include "linker.h" #include "ir_uniform.h" #include "link_uniform_block_active_visitor.h" #include "util/hash_table.h" #include "program.h" +#include "main/errors.h" +#include "main/mtypes.h" namespace { class ubo_visitor : public program_resource_visitor { public: ubo_visitor(void *mem_ctx, gl_uniform_buffer_variable *variables, - unsigned num_variables) + unsigned num_variables, struct gl_shader_program *prog, + bool use_std430_as_default) : index(0), offset(0), buffer_size(0), variables(variables), - num_variables(num_variables), mem_ctx(mem_ctx), is_array_instance(false) + num_variables(num_variables), mem_ctx(mem_ctx), + is_array_instance(false), prog(prog), + use_std430_as_default(use_std430_as_default) { /* empty */ } @@ -46,7 +50,8 @@ public: this->offset = 0; this->buffer_size = 0; this->is_array_instance = strchr(name, ']') != NULL; - this->program_resource_visitor::process(type, name); + this->program_resource_visitor::process(type, name, + use_std430_as_default); } unsigned index; @@ -56,20 +61,14 @@ public: unsigned num_variables; void *mem_ctx; bool is_array_instance; + struct gl_shader_program *prog; private: - virtual void visit_field(const glsl_type *type, const char *name, - bool row_major) - { - (void) type; - (void) name; - (void) row_major; - assert(!"Should not get here."); - } - virtual void enter_record(const glsl_type *type, const char *, - bool row_major, const unsigned packing) { - assert(type->is_record()); + bool row_major, + const enum glsl_interface_packing packing) + { + assert(type->is_struct()); if (packing == GLSL_INTERFACE_PACKING_STD430) this->offset = glsl_align( this->offset, type->std430_base_alignment(row_major)); @@ -79,15 +78,17 @@ private: } virtual void leave_record(const glsl_type *type, const char *, - bool row_major, const unsigned packing) { - assert(type->is_record()); + bool row_major, + const enum glsl_interface_packing packing) + { + assert(type->is_struct()); /* If this is the last field of a structure, apply rule #9. The - * GL_ARB_uniform_buffer_object spec says: + * ARB_uniform_buffer_object spec says: * - * "The structure may have padding at the end; the base offset of - * the member following the sub-structure is rounded up to the next - * multiple of the base alignment of the structure." + * The structure may have padding at the end; the base offset of the + * member following the sub-structure is rounded up to the next + * multiple of the base alignment of the structure. */ if (packing == GLSL_INTERFACE_PACKING_STD430) this->offset = glsl_align( @@ -97,9 +98,14 @@ private: this->offset, type->std140_base_alignment(row_major)); } + virtual void set_buffer_offset(unsigned offset) + { + this->offset = offset; + } + virtual void visit_field(const glsl_type *type, const char *name, bool row_major, const glsl_type *, - const unsigned packing, + const enum glsl_interface_packing packing, bool last_field) { assert(this->index < this->num_variables); @@ -131,19 +137,26 @@ private: unsigned alignment = 0; unsigned size = 0; - /* From ARB_program_interface_query: + /* The ARB_program_interface_query spec says: * - * "If the final member of an active shader storage block is array - * with no declared size, the minimum buffer size is computed - * assuming the array was declared as an array with one element." + * If the final member of an active shader storage block is array + * with no declared size, the minimum buffer size is computed + * assuming the array was declared as an array with one element. * - * For that reason, we use the base type of the unsized array to calculate - * its size. We don't need to check if the unsized array is the last member - * of a shader storage block (that check was already done by the parser). + * For that reason, we use the base type of the unsized array to + * calculate its size. We don't need to check if the unsized array is + * the last member of a shader storage block (that check was already + * done by the parser). */ const glsl_type *type_for_size = type; if (type->is_unsized_array()) { - assert(last_field); + if (!last_field) { + linker_error(prog, "unsized array `%s' definition: " + "only last member of a shader storage block " + "can be defined as unsized array", + name); + } + type_for_size = type->without_array(); } @@ -160,18 +173,20 @@ private: this->offset += size; - /* From the GL_ARB_uniform_buffer_object spec: + /* The ARB_uniform_buffer_object spec says: * - * "For uniform blocks laid out according to [std140] rules, the - * minimum buffer object size returned by the - * UNIFORM_BLOCK_DATA_SIZE query is derived by taking the offset of - * the last basic machine unit consumed by the last uniform of the - * uniform block (including any end-of-array or end-of-structure - * padding), adding one, and rounding up to the next multiple of - * the base alignment required for a vec4." + * For uniform blocks laid out according to [std140] rules, the + * minimum buffer object size returned by the UNIFORM_BLOCK_DATA_SIZE + * query is derived by taking the offset of the last basic machine + * unit consumed by the last uniform of the uniform block (including + * any end-of-array or end-of-structure padding), adding one, and + * rounding up to the next multiple of the base alignment required + * for a vec4. */ this->buffer_size = glsl_align(this->offset, 16); } + + bool use_std430_as_default; }; class count_block_size : public program_resource_visitor { @@ -184,12 +199,13 @@ public: unsigned num_active_uniforms; private: - virtual void visit_field(const glsl_type *type, const char *name, - bool row_major) + virtual void visit_field(const glsl_type * /* type */, + const char * /* name */, + bool /* row_major */, + const glsl_type * /* record_type */, + const enum glsl_interface_packing, + bool /* last_field */) { - (void) type; - (void) name; - (void) row_major; this->num_active_uniforms++; } }; @@ -201,66 +217,97 @@ struct block { bool has_instance_name; }; +static void process_block_array_leaf(const char *name, gl_uniform_block *blocks, + ubo_visitor *parcel, + gl_uniform_buffer_variable *variables, + const struct link_uniform_block_active *const b, + unsigned *block_index, + unsigned *binding_offset, + unsigned linearized_index, + struct gl_context *ctx, + struct gl_shader_program *prog); + +/** + * + * \param first_index Value of \c block_index for the first element of the + * array. + */ static void process_block_array(struct uniform_block_array_elements *ub_array, char **name, size_t name_length, gl_uniform_block *blocks, ubo_visitor *parcel, gl_uniform_buffer_variable *variables, const struct link_uniform_block_active *const b, unsigned *block_index, unsigned *binding_offset, - struct gl_context *ctx, struct gl_shader_program *prog) + struct gl_context *ctx, struct gl_shader_program *prog, + unsigned first_index) { - if (ub_array) { - for (unsigned j = 0; j < ub_array->num_array_elements; j++) { - size_t new_length = name_length; + for (unsigned j = 0; j < ub_array->num_array_elements; j++) { + size_t new_length = name_length; - /* Append the subscript to the current variable name */ - ralloc_asprintf_rewrite_tail(name, &new_length, "[%u]", - ub_array->array_elements[j]); + /* Append the subscript to the current variable name */ + ralloc_asprintf_rewrite_tail(name, &new_length, "[%u]", + ub_array->array_elements[j]); + if (ub_array->array) { process_block_array(ub_array->array, name, new_length, blocks, parcel, variables, b, block_index, - binding_offset, ctx, prog); + binding_offset, ctx, prog, first_index); + } else { + process_block_array_leaf(*name, blocks, + parcel, variables, b, block_index, + binding_offset, *block_index - first_index, + ctx, prog); } - } else { - unsigned i = *block_index; - const glsl_type *type = b->type->without_array(); + } +} - blocks[i].Name = ralloc_strdup(blocks, *name); - blocks[i].Uniforms = &variables[(*parcel).index]; +static void +process_block_array_leaf(const char *name, + gl_uniform_block *blocks, + ubo_visitor *parcel, gl_uniform_buffer_variable *variables, + const struct link_uniform_block_active *const b, + unsigned *block_index, unsigned *binding_offset, + unsigned linearized_index, + struct gl_context *ctx, struct gl_shader_program *prog) +{ + unsigned i = *block_index; + const glsl_type *type = b->type->without_array(); - /* The GL_ARB_shading_language_420pack spec says: - * - * "If the binding identifier is used with a uniform block - * instanced as an array then the first element of the array - * takes the specified block binding and each subsequent - * element takes the next consecutive uniform block binding - * point." - */ - blocks[i].Binding = (b->has_binding) ? b->binding + *binding_offset : 0; + blocks[i].Name = ralloc_strdup(blocks, name); + blocks[i].Uniforms = &variables[(*parcel).index]; - blocks[i].UniformBufferSize = 0; - blocks[i]._Packing = gl_uniform_block_packing(type->interface_packing); + /* The ARB_shading_language_420pack spec says: + * + * If the binding identifier is used with a uniform block instanced as + * an array then the first element of the array takes the specified + * block binding and each subsequent element takes the next consecutive + * uniform block binding point. + */ + blocks[i].Binding = (b->has_binding) ? b->binding + *binding_offset : 0; - parcel->process(type, blocks[i].Name); + blocks[i].UniformBufferSize = 0; + blocks[i]._Packing = glsl_interface_packing(type->interface_packing); + blocks[i]._RowMajor = type->get_interface_row_major(); + blocks[i].linearized_array_index = linearized_index; - blocks[i].UniformBufferSize = parcel->buffer_size; + parcel->process(type, b->has_instance_name ? blocks[i].Name : ""); - /* Check SSBO size is lower than maximum supported size for SSBO */ - if (b->is_shader_storage && - parcel->buffer_size > ctx->Const.MaxShaderStorageBlockSize) { - linker_error(prog, "shader storage block `%s' has size %d, " - "which is larger than than the maximum allowed (%d)", - b->type->name, - parcel->buffer_size, - ctx->Const.MaxShaderStorageBlockSize); - } - blocks[i].NumUniforms = - (unsigned)(ptrdiff_t)(&variables[parcel->index] - blocks[i].Uniforms); - blocks[i].IsShaderStorage = b->is_shader_storage; + blocks[i].UniformBufferSize = parcel->buffer_size; - *block_index = *block_index + 1; - *binding_offset = *binding_offset + 1; + /* Check SSBO size is lower than maximum supported size for SSBO */ + if (b->is_shader_storage && + parcel->buffer_size > ctx->Const.MaxShaderStorageBlockSize) { + linker_error(prog, "shader storage block `%s' has size %d, " + "which is larger than the maximum allowed (%d)", + b->type->name, + parcel->buffer_size, + ctx->Const.MaxShaderStorageBlockSize); } + blocks[i].NumUniforms = + (unsigned)(ptrdiff_t)(&variables[parcel->index] - blocks[i].Uniforms); + + *block_index = *block_index + 1; + *binding_offset = *binding_offset + 1; } /* This function resizes the array types of the block so that later we can use @@ -286,13 +333,76 @@ resize_block_array(const glsl_type *type, } } -unsigned +static void +create_buffer_blocks(void *mem_ctx, struct gl_context *ctx, + struct gl_shader_program *prog, + struct gl_uniform_block **out_blks, unsigned num_blocks, + struct hash_table *block_hash, unsigned num_variables, + bool create_ubo_blocks) +{ + if (num_blocks == 0) { + assert(num_variables == 0); + return; + } + + assert(num_variables != 0); + + /* Allocate storage to hold all of the information related to uniform + * blocks that can be queried through the API. + */ + struct gl_uniform_block *blocks = + rzalloc_array(mem_ctx, gl_uniform_block, num_blocks); + gl_uniform_buffer_variable *variables = + ralloc_array(blocks, gl_uniform_buffer_variable, num_variables); + + /* Add each variable from each uniform block to the API tracking + * structures. + */ + ubo_visitor parcel(blocks, variables, num_variables, prog, + ctx->Const.UseSTD430AsDefaultPacking); + + unsigned i = 0; + hash_table_foreach (block_hash, entry) { + const struct link_uniform_block_active *const b = + (const struct link_uniform_block_active *) entry->data; + const glsl_type *block_type = b->type; + + if ((create_ubo_blocks && !b->is_shader_storage) || + (!create_ubo_blocks && b->is_shader_storage)) { + + unsigned binding_offset = 0; + if (b->array != NULL) { + char *name = ralloc_strdup(NULL, + block_type->without_array()->name); + size_t name_length = strlen(name); + + assert(b->has_instance_name); + process_block_array(b->array, &name, name_length, blocks, &parcel, + variables, b, &i, &binding_offset, ctx, prog, + i); + ralloc_free(name); + } else { + process_block_array_leaf(block_type->name, blocks, &parcel, + variables, b, &i, &binding_offset, + 0, ctx, prog); + } + } + } + + *out_blks = blocks; + + assert(parcel.index == num_variables); +} + +void link_uniform_blocks(void *mem_ctx, struct gl_context *ctx, struct gl_shader_program *prog, - struct gl_shader **shader_list, - unsigned num_shaders, - struct gl_uniform_block **blocks_ret) + struct gl_linked_shader *shader, + struct gl_uniform_block **ubo_blocks, + unsigned *num_ubo_blocks, + struct gl_uniform_block **ssbo_blocks, + unsigned *num_ssbo_blocks) { /* This hash table will track all of the uniform blocks that have been * encountered. Since blocks with the same block-name must be the same, @@ -305,23 +415,19 @@ link_uniform_blocks(void *mem_ctx, if (block_hash == NULL) { _mesa_error_no_memory(__func__); linker_error(prog, "out of memory\n"); - return 0; + return; } - /* Determine which uniform blocks are active. - */ + /* Determine which uniform blocks are active. */ link_uniform_block_active_visitor v(mem_ctx, block_hash, prog); - for (unsigned i = 0; i < num_shaders; i++) { - visit_list_elements(&v, shader_list[i]->ir); - } + visit_list_elements(&v, shader->ir); /* Count the number of active uniform blocks. Count the total number of * active slots in those uniform blocks. */ - unsigned num_blocks = 0; - unsigned num_variables = 0; + unsigned num_ubo_variables = 0; + unsigned num_ssbo_variables = 0; count_block_size block_size; - struct hash_entry *entry; hash_table_foreach (block_hash, entry) { struct link_uniform_block_active *const b = @@ -337,117 +443,52 @@ link_uniform_blocks(void *mem_ctx, } block_size.num_active_uniforms = 0; - block_size.process(b->type->without_array(), ""); + block_size.process(b->type->without_array(), "", + ctx->Const.UseSTD430AsDefaultPacking); if (b->array != NULL) { unsigned aoa_size = b->type->arrays_of_arrays_size(); - num_blocks += aoa_size; - num_variables += aoa_size * block_size.num_active_uniforms; - } else { - num_blocks++; - num_variables += block_size.num_active_uniforms; - } - - } - - if (num_blocks == 0) { - assert(num_variables == 0); - _mesa_hash_table_destroy(block_hash, NULL); - return 0; - } - - assert(num_variables != 0); - - /* Allocate storage to hold all of the informatation related to uniform - * blocks that can be queried through the API. - */ - gl_uniform_block *blocks = - ralloc_array(mem_ctx, gl_uniform_block, num_blocks); - gl_uniform_buffer_variable *variables = - ralloc_array(blocks, gl_uniform_buffer_variable, num_variables); - - /* Add each variable from each uniform block to the API tracking - * structures. - */ - unsigned i = 0; - ubo_visitor parcel(blocks, variables, num_variables); - - STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_STD140) - == unsigned(ubo_packing_std140)); - STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_SHARED) - == unsigned(ubo_packing_shared)); - STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_PACKED) - == unsigned(ubo_packing_packed)); - STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_STD430) - == unsigned(ubo_packing_std430)); - - hash_table_foreach (block_hash, entry) { - const struct link_uniform_block_active *const b = - (const struct link_uniform_block_active *) entry->data; - const glsl_type *block_type = b->type; - - if (b->array != NULL) { - unsigned binding_offset = 0; - char *name = ralloc_strdup(NULL, block_type->without_array()->name); - size_t name_length = strlen(name); - - assert(b->has_instance_name); - process_block_array(b->array, &name, name_length, blocks, &parcel, - variables, b, &i, &binding_offset, ctx, prog); - ralloc_free(name); + if (b->is_shader_storage) { + *num_ssbo_blocks += aoa_size; + num_ssbo_variables += aoa_size * block_size.num_active_uniforms; + } else { + *num_ubo_blocks += aoa_size; + num_ubo_variables += aoa_size * block_size.num_active_uniforms; + } } else { - blocks[i].Name = ralloc_strdup(blocks, block_type->name); - blocks[i].Uniforms = &variables[parcel.index]; - blocks[i].Binding = (b->has_binding) ? b->binding : 0; - blocks[i].UniformBufferSize = 0; - blocks[i]._Packing = - gl_uniform_block_packing(block_type->interface_packing); - - parcel.process(block_type, - b->has_instance_name ? block_type->name : ""); - - blocks[i].UniformBufferSize = parcel.buffer_size; - - /* Check SSBO size is lower than maximum supported size for SSBO */ - if (b->is_shader_storage && - parcel.buffer_size > ctx->Const.MaxShaderStorageBlockSize) { - linker_error(prog, "shader storage block `%s' has size %d, " - "which is larger than than the maximum allowed (%d)", - block_type->name, - parcel.buffer_size, - ctx->Const.MaxShaderStorageBlockSize); + if (b->is_shader_storage) { + (*num_ssbo_blocks)++; + num_ssbo_variables += block_size.num_active_uniforms; + } else { + (*num_ubo_blocks)++; + num_ubo_variables += block_size.num_active_uniforms; } - blocks[i].NumUniforms = - (unsigned)(ptrdiff_t)(&variables[parcel.index] - blocks[i].Uniforms); - - blocks[i].IsShaderStorage = b->is_shader_storage; - - i++; } + } - assert(parcel.index == num_variables); + create_buffer_blocks(mem_ctx, ctx, prog, ubo_blocks, *num_ubo_blocks, + block_hash, num_ubo_variables, true); + create_buffer_blocks(mem_ctx, ctx, prog, ssbo_blocks, *num_ssbo_blocks, + block_hash, num_ssbo_variables, false); _mesa_hash_table_destroy(block_hash, NULL); - - *blocks_ret = blocks; - return num_blocks; } -bool +static bool link_uniform_blocks_are_compatible(const gl_uniform_block *a, - const gl_uniform_block *b) + const gl_uniform_block *b) { assert(strcmp(a->Name, b->Name) == 0); /* Page 35 (page 42 of the PDF) in section 4.3.7 of the GLSL 1.50 spec says: * - * "Matched block names within an interface (as defined above) must - * match in terms of having the same number of declarations with the - * same sequence of types and the same sequence of member names, as - * well as having the same member-wise layout qualification....if a - * matching block is declared as an array, then the array sizes must - * also match... Any mismatch will generate a link error." + * Matched block names within an interface (as defined above) must match + * in terms of having the same number of declarations with the same + * sequence of types and the same sequence of member names, as well as + * having the same member-wise layout qualification....if a matching + * block is declared as an array, then the array sizes must also + * match... Any mismatch will generate a link error. * * Arrays are not yet supported, so there is no check for that. */ @@ -457,16 +498,75 @@ link_uniform_blocks_are_compatible(const gl_uniform_block *a, if (a->_Packing != b->_Packing) return false; + if (a->_RowMajor != b->_RowMajor) + return false; + + if (a->Binding != b->Binding) + return false; + for (unsigned i = 0; i < a->NumUniforms; i++) { if (strcmp(a->Uniforms[i].Name, b->Uniforms[i].Name) != 0) - return false; + return false; if (a->Uniforms[i].Type != b->Uniforms[i].Type) - return false; + return false; if (a->Uniforms[i].RowMajor != b->Uniforms[i].RowMajor) - return false; + return false; } return true; } + +/** + * Merges a uniform block into an array of uniform blocks that may or + * may not already contain a copy of it. + * + * Returns the index of the new block in the array. + */ +int +link_cross_validate_uniform_block(void *mem_ctx, + struct gl_uniform_block **linked_blocks, + unsigned int *num_linked_blocks, + struct gl_uniform_block *new_block) +{ + for (unsigned int i = 0; i < *num_linked_blocks; i++) { + struct gl_uniform_block *old_block = &(*linked_blocks)[i]; + + if (strcmp(old_block->Name, new_block->Name) == 0) + return link_uniform_blocks_are_compatible(old_block, new_block) + ? i : -1; + } + + *linked_blocks = reralloc(mem_ctx, *linked_blocks, + struct gl_uniform_block, + *num_linked_blocks + 1); + int linked_block_index = (*num_linked_blocks)++; + struct gl_uniform_block *linked_block = &(*linked_blocks)[linked_block_index]; + + memcpy(linked_block, new_block, sizeof(*new_block)); + linked_block->Uniforms = ralloc_array(*linked_blocks, + struct gl_uniform_buffer_variable, + linked_block->NumUniforms); + + memcpy(linked_block->Uniforms, + new_block->Uniforms, + sizeof(*linked_block->Uniforms) * linked_block->NumUniforms); + + linked_block->Name = ralloc_strdup(*linked_blocks, linked_block->Name); + + for (unsigned int i = 0; i < linked_block->NumUniforms; i++) { + struct gl_uniform_buffer_variable *ubo_var = + &linked_block->Uniforms[i]; + + if (ubo_var->Name == ubo_var->IndexName) { + ubo_var->Name = ralloc_strdup(*linked_blocks, ubo_var->Name); + ubo_var->IndexName = ubo_var->Name; + } else { + ubo_var->Name = ralloc_strdup(*linked_blocks, ubo_var->Name); + ubo_var->IndexName = ralloc_strdup(*linked_blocks, ubo_var->IndexName); + } + } + + return linked_block_index; +}