From 10b816d27e2e9f744a29beec294774c1d24f4f54 Mon Sep 17 00:00:00 2001 From: Timothy Arceri Date: Wed, 8 Jan 2020 11:56:36 +1100 Subject: [PATCH] glsl: add name support to nir uniform linker MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Name support is optional for spirv support but is required for glsl support. Reviewed-by: Alejandro Piñeiro Part-of: --- src/compiler/glsl/gl_nir_link_uniforms.c | 290 +++++++++++++++++++---- 1 file changed, 248 insertions(+), 42 deletions(-) diff --git a/src/compiler/glsl/gl_nir_link_uniforms.c b/src/compiler/glsl/gl_nir_link_uniforms.c index 311ea0dc0e3..57a5595b34d 100644 --- a/src/compiler/glsl/gl_nir_link_uniforms.c +++ b/src/compiler/glsl/gl_nir_link_uniforms.c @@ -187,6 +187,7 @@ struct nir_link_uniforms_state { int top_level_array_stride; struct type_tree_entry *current_type; + struct hash_table *uniform_hash; }; static void @@ -222,12 +223,12 @@ add_parameter(struct gl_uniform_storage *uniform, comps = 4; } - _mesa_add_parameter(params, PROGRAM_UNIFORM, NULL, comps, + _mesa_add_parameter(params, PROGRAM_UNIFORM, uniform->name, comps, glsl_get_gl_type(type), NULL, NULL, false); } } else { for (unsigned i = 0; i < num_params; i++) { - _mesa_add_parameter(params, PROGRAM_UNIFORM, NULL, 4, + _mesa_add_parameter(params, PROGRAM_UNIFORM, uniform->name, 4, glsl_get_gl_type(type), NULL, NULL, true); } } @@ -277,10 +278,161 @@ get_next_index(struct nir_link_uniforms_state *state, return index; } +static bool +find_and_update_named_uniform_storage(struct gl_context *ctx, + struct gl_shader_program *prog, + struct nir_link_uniforms_state *state, + nir_variable *var, char **name, + size_t name_length, + const struct glsl_type *type, + unsigned stage, bool *first_element) +{ + /* gl_uniform_storage can cope with one level of array, so if the type is a + * composite type or an array where each element occupies more than one + * location than we need to recursively process it. + */ + if (glsl_type_is_struct_or_ifc(type) || + (glsl_type_is_array(type) && + (glsl_type_is_array(glsl_get_array_element(type)) || + glsl_type_is_struct_or_ifc(glsl_get_array_element(type))))) { + + struct type_tree_entry *old_type = state->current_type; + state->current_type = old_type->children; + + /* Shader storage block unsized arrays: add subscript [0] to variable + * names. + */ + unsigned length = glsl_get_length(type); + if (glsl_type_is_unsized_array(type)) + length = 1; + + bool result = false; + for (unsigned i = 0; i < length; i++) { + const struct glsl_type *field_type; + size_t new_length = name_length; + + if (glsl_type_is_struct_or_ifc(type)) { + field_type = glsl_get_struct_field(type, i); + + /* Append '.field' to the current variable name. */ + if (name) { + ralloc_asprintf_rewrite_tail(name, &new_length, ".%s", + glsl_get_struct_elem_name(type, i)); + } + } else { + field_type = glsl_get_array_element(type); + + /* Append the subscript to the current variable name */ + if (name) + ralloc_asprintf_rewrite_tail(name, &new_length, "[%u]", i); + } + + result = find_and_update_named_uniform_storage(ctx, prog, state, + var, name, new_length, + field_type, stage, + first_element); + + if (glsl_type_is_struct_or_ifc(type)) + state->current_type = state->current_type->next_sibling; + + if (!result) { + state->current_type = old_type; + return false; + } + } + + state->current_type = old_type; + + return result; + } else { + struct hash_entry *entry = + _mesa_hash_table_search(state->uniform_hash, *name); + if (entry) { + unsigned i = (unsigned) (intptr_t) entry->data; + mark_stage_as_active(&prog->data->UniformStorage[i], stage); + + struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i]; + + if (*first_element && !state->var_is_in_block) { + *first_element = false; + var->data.location = uniform - prog->data->UniformStorage; + } + + unsigned values = glsl_get_component_slots(type); + const struct glsl_type *type_no_array = glsl_without_array(type); + if (glsl_type_is_sampler(type_no_array)) { + struct gl_linked_shader *sh = prog->_LinkedShaders[stage]; + bool init_idx; + unsigned sampler_index = + get_next_index(state, uniform, &state->next_sampler_index, + &init_idx); + + /* Samplers (bound or bindless) are counted as two components as + * specified by ARB_bindless_texture. + */ + state->num_shader_samplers += values / 2; + + uniform->opaque[stage].active = true; + uniform->opaque[stage].index = sampler_index; + + if (init_idx) { + const unsigned shadow = + glsl_sampler_type_is_shadow(type_no_array); + for (unsigned i = sampler_index; + i < MIN2(state->next_sampler_index, MAX_SAMPLERS); + i++) { + sh->Program->sh.SamplerTargets[i] = + glsl_get_sampler_target(type_no_array); + state->shader_samplers_used |= 1U << i; + state->shader_shadow_samplers |= shadow << i; + } + } + } else if (glsl_type_is_image(type_no_array)) { + struct gl_linked_shader *sh = prog->_LinkedShaders[stage]; + int image_index = state->next_image_index; + /* TODO: handle structs when bindless support is added */ + state->next_image_index += MAX2(1, uniform->array_elements); + + /* Images (bound or bindless) are counted as two components as + * specified by ARB_bindless_texture. + */ + state->num_shader_images += values / 2; + + uniform->opaque[stage].active = true; + uniform->opaque[stage].index = image_index; + + /* Set image access qualifiers */ + enum gl_access_qualifier image_access = + state->current_var->data.access; + const GLenum access = + (image_access & ACCESS_NON_WRITEABLE) ? + ((image_access & ACCESS_NON_READABLE) ? GL_NONE : + GL_READ_ONLY) : + ((image_access & ACCESS_NON_READABLE) ? GL_WRITE_ONLY : + GL_READ_WRITE); + for (unsigned i = image_index; + i < MIN2(state->next_image_index, MAX_IMAGE_UNIFORMS); + i++) { + sh->Program->sh.ImageAccess[i] = access; + } + } + + uniform->active_shader_mask |= 1 << stage; + + if (!state->var_is_in_block) + add_parameter(uniform, ctx, prog, type, state); + + return true; + } + } + + return false; +} + /** * Finds, returns, and updates the stage info for any uniform in UniformStorage - * defined by @var. In general this is done using the explicit location, - * except: + * defined by @var. For GLSL this is done using the name, for SPIR-V in general + * is this done using the explicit location, except: * * * UBOs/SSBOs: as they lack explicit location, binding is used to locate * them. That means that more that one entry at the uniform storage can be @@ -291,11 +443,26 @@ get_next_index(struct nir_link_uniforms_state *state, * so they are skipped. They will be handled and assigned a location later. * */ -static struct gl_uniform_storage * -find_and_update_previous_uniform_storage(struct gl_shader_program *prog, - nir_variable *var, +static bool +find_and_update_previous_uniform_storage(struct gl_context *ctx, + struct gl_shader_program *prog, + struct nir_link_uniforms_state *state, + nir_variable *var, char *name, + const struct glsl_type *type, unsigned stage) { + if (!prog->data->spirv) { + bool first_element = true; + char *name_tmp = ralloc_strdup(NULL, name); + bool r = find_and_update_named_uniform_storage(ctx, prog, state, var, + &name_tmp, + strlen(name_tmp), type, + stage, &first_element); + ralloc_free(name_tmp); + + return r; + } + if (nir_variable_is_in_block(var)) { struct gl_uniform_storage *uniform = NULL; @@ -306,6 +473,7 @@ find_and_update_previous_uniform_storage(struct gl_shader_program *prog, struct gl_uniform_block *blks = nir_variable_is_in_ubo(var) ? prog->data->UniformBlocks : prog->data->ShaderStorageBlocks; + bool result = false; for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { /* UniformStorage contains both variables from ubos and ssbos */ if ( prog->data->UniformStorage[i].is_shader_storage != @@ -321,11 +489,14 @@ find_and_update_previous_uniform_storage(struct gl_shader_program *prog, uniform = &prog->data->UniformStorage[i]; mark_stage_as_active(&prog->data->UniformStorage[i], stage); + result = true; } } } - return uniform; + if (result) + var->data.location = uniform - prog->data->UniformStorage; + return result; } /* Beyond blocks, there are still some corner cases of uniforms without @@ -334,7 +505,7 @@ find_and_update_previous_uniform_storage(struct gl_shader_program *prog, * later. */ if (var->data.location == -1) - return NULL; + return false; /* TODO: following search can be problematic with shaders with a lot of * uniforms. Would it be better to use some type of hash @@ -343,11 +514,14 @@ find_and_update_previous_uniform_storage(struct gl_shader_program *prog, if (prog->data->UniformStorage[i].remap_location == var->data.location) { mark_stage_as_active(&prog->data->UniformStorage[i], stage); - return &prog->data->UniformStorage[i]; + struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i]; + var->data.location = uniform - prog->data->UniformStorage; + add_parameter(uniform, ctx, prog, var->type, state); + return true; } } - return NULL; + return false; } static struct type_tree_entry * @@ -400,6 +574,12 @@ free_type_tree(struct type_tree_entry *entry) free(entry); } +static void +hash_free_uniform_name(struct hash_entry *entry) +{ + free((void*)entry->key); +} + /** * Creates the neccessary entries in UniformStorage for the uniform. Returns * the number of locations used or -1 on failure. @@ -413,7 +593,8 @@ nir_link_uniform(struct gl_context *ctx, const struct glsl_type *parent_type, unsigned index_in_parent, int location, - struct nir_link_uniforms_state *state) + struct nir_link_uniforms_state *state, + char **name, size_t name_length) { struct gl_uniform_storage *uniform = NULL; @@ -448,6 +629,7 @@ nir_link_uniform(struct gl_context *ctx, for (unsigned i = 0; i < glsl_get_length(type); i++) { const struct glsl_type *field_type; + size_t new_length = name_length; if (glsl_type_is_struct_or_ifc(type)) { field_type = glsl_get_struct_field(type, i); @@ -459,13 +641,23 @@ nir_link_uniform(struct gl_context *ctx, state->offset = struct_base_offset + glsl_get_struct_field_offset(type, i); } + + /* Append '.field' to the current variable name. */ + if (name) { + ralloc_asprintf_rewrite_tail(name, &new_length, ".%s", + glsl_get_struct_elem_name(type, i)); + } } else { field_type = glsl_get_array_element(type); + + /* Append the subscript to the current variable name */ + if (name) + ralloc_asprintf_rewrite_tail(name, &new_length, "[%u]", i); } int entries = nir_link_uniform(ctx, prog, stage_program, stage, field_type, type, i, location, - state); + state, name, new_length); if (entries == -1) return -1; @@ -497,11 +689,9 @@ nir_link_uniform(struct gl_context *ctx, /* Initialize its members */ memset(uniform, 0x00, sizeof(struct gl_uniform_storage)); - /* ARB_gl_spirv: names are considered optional debug info, so the linker - * needs to work without them, and returning them is optional. For - * simplicity we ignore names. - */ - uniform->name = NULL; + + uniform->name = + name ? ralloc_strdup(prog->data->UniformStorage, *name) : NULL; const struct glsl_type *type_no_array = glsl_without_array(type); if (glsl_type_is_array(type)) { @@ -683,6 +873,12 @@ nir_link_uniform(struct gl_context *ctx, if (!state->var_is_in_block) add_parameter(uniform, ctx, prog, type, state); + if (name) { + _mesa_hash_table_insert(state->uniform_hash, strdup(*name), + (void *) (intptr_t) + (prog->data->NumUniformStorage - 1)); + } + return MAX2(uniform->array_elements, 1); } } @@ -699,6 +895,8 @@ gl_nir_link_uniforms(struct gl_context *ctx, /* Iterate through all linked shaders */ struct nir_link_uniforms_state state = {0,}; + state.uniform_hash = _mesa_hash_table_create(NULL, _mesa_hash_string, + _mesa_key_string_equal); for (unsigned shader_type = 0; shader_type < MESA_SHADER_STAGES; shader_type++) { struct gl_linked_shader *sh = prog->_LinkedShaders[shader_type]; @@ -718,28 +916,7 @@ gl_nir_link_uniforms(struct gl_context *ctx, state.params = fill_parameters ? sh->Program->Parameters : NULL; nir_foreach_variable(var, &nir->uniforms) { - struct gl_uniform_storage *uniform = NULL; - state.current_var = var; - - /* Check if the uniform has been processed already for - * other stage. If so, validate they are compatible and update - * the active stage mask. - */ - uniform = find_and_update_previous_uniform_storage(prog, var, shader_type); - if (uniform) { - var->data.location = uniform - prog->data->UniformStorage; - - if (!state.var_is_in_block) - add_parameter(uniform, ctx, prog, var->type, &state); - - continue; - } - - int location = var->data.location; - /* From now on the variable’s location will be its uniform index */ - var->data.location = prog->data->NumUniformStorage; - state.offset = 0; state.var_is_in_block = nir_variable_is_in_block(var); state.top_level_array_size = 0; @@ -779,21 +956,48 @@ gl_nir_link_uniforms(struct gl_context *ctx, * uniforms of a ubo, or the variables of a ssbo, we need to treat * arrays of instance as a single block. */ + char *name; const struct glsl_type *type = var->type; - if (state.var_is_in_block && glsl_type_is_array(type)) { - type = glsl_without_array(type); + if (state.var_is_in_block && + ((!prog->data->spirv && glsl_without_array(type) == var->interface_type) || + (prog->data->spirv && type == var->interface_type))) { + type = glsl_without_array(var->type); + name = ralloc_strdup(NULL, glsl_get_type_name(type)); + } else { + name = ralloc_strdup(NULL, var->name); } struct type_tree_entry *type_tree = build_type_tree_for_type(type); state.current_type = type_tree; + /* Check if the uniform has been processed already for + * other stage. If so, validate they are compatible and update + * the active stage mask. + */ + if (find_and_update_previous_uniform_storage(ctx, prog, &state, var, + name, type, shader_type)) { + ralloc_free(name); + free_type_tree(type_tree); + continue; + } + + int location = var->data.location; + /* From now on the variable’s location will be its uniform index */ + if (!state.var_is_in_block) + var->data.location = prog->data->NumUniformStorage; + else + location = -1; + int res = nir_link_uniform(ctx, prog, sh->Program, shader_type, type, NULL, 0, location, - &state); + &state, + !prog->data->spirv ? &name : NULL, + !prog->data->spirv ? strlen(name) : 0); free_type_tree(type_tree); + ralloc_free(name); if (res == -1) return false; @@ -830,5 +1034,7 @@ gl_nir_link_uniforms(struct gl_context *ctx, nir_setup_uniform_remap_tables(ctx, prog); gl_nir_set_uniform_initializers(ctx, prog); + _mesa_hash_table_destroy(state.uniform_hash, hash_free_uniform_name); + return true; } -- 2.30.2