glsl: add name support to nir uniform linker

Name support is optional for spirv support but is required for glsl
support.

Reviewed-by: Alejandro Piñeiro <apinheiro@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4050>
This commit is contained in:
Timothy Arceri
2020-01-08 11:56:36 +11:00
committed by Marge Bot
parent aa9b457062
commit 10b816d27e
+248 -42
View File
@@ -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 variables 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 variables 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;
}