[Mesa-dev] [PATCH] nir/linker: Add nir_link_uniforms()
Timothy Arceri
tarceri at itsqueeze.com
Sun Apr 29 08:13:44 UTC 2018
On 29/04/18 16:48, Alejandro Piñeiro wrote:
> From: Eduardo Lima Mitev <elima at igalia.com>
>
> This function will be the entry point for linking the uniforms from
> the nir_shader objects associated with the gl_linked_shaders of a
> program.
>
> This patch includes initial support for linking uniforms from NIR
> shaders. It is tailored for the ARB_gl_spirv needs, and it is far from
> complete, but it should handle most cases of uniforms, array
> uniforms, structs, samplers and images.
>
> There are some FIXMEs related to specific features that will be
> implemented in following patches, like atomic counters, UBOs and
> SSBOs.
>
> Also, note that ARB_gl_spirv makes mandatory explicit location for
> normal uniforms, so this code only handles uniforms with explicit
> location. But there are cases, like uniform atomic counters, that
> doesn't have a location from the OpenGL point of view (they have a
> binding), but that Mesa assign internally a location. That will be
> handled on following patches.
>
> A nir_linker.h file is also added. More NIR-linking related API will
> be added in subsequent patches and those will include stuff from Mesa,
> so reusing nir.h didn't seem a good idea.
These files should actually be src/compiler/glsl/nir_link_uniforms.c etc
these are not general purpose nir helpers they are GLSL specific.
>
> v2: update var->data.location with UniformStorage index when the
> storage was set up on a previous stage (Neil)
>
> Signed-off-by: Eduardo Lima <elima at igalia.com>
> Signed-off-by: Neil Roberts <nroberts at igalia.com
> Signed-off-by: Alejandro Piñeiro <apinheiro at igalia.com>
> ---
> src/compiler/Makefile.sources | 2 +
> src/compiler/nir/meson.build | 2 +
> src/compiler/nir/nir_link_uniforms.c | 461 +++++++++++++++++++++++++++++++++++
> src/compiler/nir/nir_linker.h | 41 ++++
> 4 files changed, 506 insertions(+)
> create mode 100644 src/compiler/nir/nir_link_uniforms.c
> create mode 100644 src/compiler/nir/nir_linker.h
>
> diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources
> index bb86972ea1a..dba70a5e5a1 100644
> --- a/src/compiler/Makefile.sources
> +++ b/src/compiler/Makefile.sources
> @@ -206,6 +206,8 @@ NIR_FILES = \
> nir/nir_inline_functions.c \
> nir/nir_instr_set.c \
> nir/nir_instr_set.h \
> + nir/nir_link_uniforms.c \
> + nir/nir_linker.h \
> nir/nir_linking_helpers.c \
> nir/nir_liveness.c \
> nir/nir_loop_analyze.c \
> diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build
> index b28a565d0c6..baf5682d388 100644
> --- a/src/compiler/nir/meson.build
> +++ b/src/compiler/nir/meson.build
> @@ -99,6 +99,8 @@ files_libnir = files(
> 'nir_inline_functions.c',
> 'nir_instr_set.c',
> 'nir_instr_set.h',
> + 'nir_link_uniforms.c',
> + 'nir_linker.h',
> 'nir_linking_helpers.c',
> 'nir_liveness.c',
> 'nir_loop_analyze.c',
> diff --git a/src/compiler/nir/nir_link_uniforms.c b/src/compiler/nir/nir_link_uniforms.c
> new file mode 100644
> index 00000000000..450a5ce2e21
> --- /dev/null
> +++ b/src/compiler/nir/nir_link_uniforms.c
> @@ -0,0 +1,461 @@
> +/*
> + * Copyright © 2018 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include "nir.h"
> +#include "nir_linker.h"
> +#include "compiler/glsl/ir_uniform.h" /* for gl_uniform_storage */
> +#include "compiler/linker_util.h"
> +#include "main/context.h"
> +#include "main/mtypes.h"
> +
> +/* This file do the common link for GLSL uniforms, using NIR, instead of IR as
> + * the counter-part glsl/link_uniforms.cpp
> + *
> + * Also note that this is tailored for ARB_gl_spirv needs and particularities
> + * (like need to work/link without name available, explicit location for
> + * normal uniforms as mandatory, and so on).
> + */
> +
> +static void
> +nir_setup_uniform_remap_tables(struct gl_context *ctx,
> + struct gl_shader_program *prog)
> +{
> + prog->UniformRemapTable = rzalloc_array(prog,
> + struct gl_uniform_storage *,
> + prog->NumUniformRemapTable);
> + union gl_constant_value *data =
> + rzalloc_array(prog->data,
> + union gl_constant_value, prog->data->NumUniformDataSlots);
> + if (!prog->UniformRemapTable || !data) {
> + linker_error(prog, "Out of memory during linking.\n");
> + return;
> + }
> + prog->data->UniformDataSlots = data;
> +
> + unsigned data_pos = 0;
> +
> + /* Reserve all the explicit locations of the active uniforms. */
> + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
> + struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
> +
> + /* How many new entries for this uniform? */
> + const unsigned entries = MAX2(1, uniform->array_elements);
> + unsigned num_slots = glsl_get_component_slots(uniform->type);
> +
> + uniform->storage = &data[data_pos];
> +
> + /* Set remap table entries point to correct gl_uniform_storage. */
> + for (unsigned j = 0; j < entries; j++) {
> + unsigned element_loc = uniform->remap_location + j;
> + prog->UniformRemapTable[element_loc] = uniform;
> +
> + data_pos += num_slots;
> + }
> + }
> +}
> +
> +static struct gl_uniform_storage *
> +find_previous_uniform_storage(struct gl_shader_program *prog,
> + int location)
> +{
> + /* This would only work for uniform with explicit location, as all the
> + * uniforms without location (ie: atomic counters) would have a initial
> + * location equal to -1. We early return in that case.
> + */
> + if (location == -1)
> + return NULL;
> +
> + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++)
> + if (prog->data->UniformStorage[i].remap_location == location)
> + return &prog->data->UniformStorage[i];
> +
> + return NULL;
> +}
> +
> +/* Used to build a tree representing the glsl_type so that we can have a place
> + * to store the next index for opaque types. Array types are expanded so that
> + * they have a single child which is used for all elements of the array.
> + * Struct types have a child for each member. The tree is walked while
> + * processing a uniform so that we can recognise when an opaque type is
> + * encountered a second time in order to reuse the same range of indices that
> + * was reserved the first time. That way the sampler indices can be arranged
> + * so that members of an array are placed sequentially even if the array is an
> + * array of structs containing other opaque members.
> + */
> +struct type_tree_entry {
> + /* For opaque types, this will be the next index to use. If we haven’t
> + * encountered this member yet, it will be UINT_MAX.
> + */
> + unsigned next_index;
> + unsigned array_size;
> + struct type_tree_entry *parent;
> + struct type_tree_entry *next_sibling;
> + struct type_tree_entry *children;
> +};
> +
> +struct nir_link_uniforms_state {
> + /* per-whole program */
> + unsigned num_hidden_uniforms;
> + unsigned num_values;
> + unsigned max_uniform_location;
> + unsigned next_sampler_index;
> + unsigned next_image_index;
> +
> + /* per-shader stage */
> + unsigned num_shader_samplers;
> + unsigned num_shader_images;
> + unsigned num_shader_uniform_components;
> + unsigned shader_samplers_used;
> + unsigned shader_shadow_samplers;
> +
> + nir_variable *current_var;
> +
> + struct type_tree_entry *current_type;
> +};
> +
> +static struct type_tree_entry *
> +build_type_tree_for_type(const struct glsl_type *type)
> +{
> + struct type_tree_entry *entry = malloc(sizeof *entry);
> +
> + entry->array_size = 1;
> + entry->next_index = UINT_MAX;
> + entry->children = NULL;
> + entry->next_sibling = NULL;
> + entry->parent = NULL;
> +
> + if (glsl_type_is_array(type)) {
> + entry->array_size = glsl_get_length(type);
> + entry->children = build_type_tree_for_type(glsl_get_array_element(type));
> + entry->children->parent = entry;
> + } else if (glsl_type_is_struct(type)) {
> + struct type_tree_entry *last = NULL;
> +
> + for (unsigned i = 0; i < glsl_get_length(type); i++) {
> + const struct glsl_type *field_type = glsl_get_struct_field(type, i);
> + struct type_tree_entry *field_entry =
> + build_type_tree_for_type(field_type);
> +
> + if (last == NULL)
> + entry->children = field_entry;
> + else
> + last->next_sibling = field_entry;
> +
> + field_entry->parent = entry;
> +
> + last = field_entry;
> + }
> + }
> +
> + return entry;
> +}
> +
> +static void
> +free_type_tree(struct type_tree_entry *entry)
> +{
> + struct type_tree_entry *p, *next;
> +
> + for (p = entry->children; p; p = next) {
> + next = p->next_sibling;
> + free_type_tree(p);
> + }
> +
> + free(entry);
> +}
> +
> +static unsigned
> +get_next_index(struct nir_link_uniforms_state *state,
> + const struct gl_uniform_storage *uniform,
> + unsigned *next_index)
> +{
> + /* If we’ve already calculated an index for this member then we can just
> + * offset from there.
> + */
> + if (state->current_type->next_index == UINT_MAX) {
> + /* Otherwise we need to reserve enough indices for all of the arrays
> + * enclosing this member.
> + */
> +
> + unsigned array_size = 1;
> +
> + for (const struct type_tree_entry *p = state->current_type;
> + p;
> + p = p->parent) {
> + array_size *= p->array_size;
> + }
> +
> + state->current_type->next_index = *next_index;
> + *next_index += array_size;
> + }
> +
> + unsigned index = state->current_type->next_index;
> +
> + state->current_type->next_index += MAX2(1, uniform->array_elements);
> +
> + return index;
> +}
> +
> +
> +/**
> + * Creates the neccessary entries in UniformStorage for the uniform. Returns
> + * the number of locations used or -1 on failure.
> + */
> +static int
> +nir_link_uniform(struct gl_context *ctx,
> + struct gl_shader_program *prog,
> + struct gl_program *stage_program,
> + gl_shader_stage stage,
> + const struct glsl_type *type,
> + int location,
> + struct nir_link_uniforms_state *state)
> +{
> + struct gl_uniform_storage *uniform = NULL;
> +
> + /* 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(type) ||
> + (glsl_type_is_array(type) &&
> + (glsl_type_is_array(glsl_get_array_element(type)) ||
> + glsl_type_is_struct(glsl_get_array_element(type))))) {
> + int location_count = 0;
> + struct type_tree_entry *old_type = state->current_type;
> +
> + state->current_type = old_type->children;
> +
> + for (unsigned i = 0; i < glsl_get_length(type); i++) {
> + const struct glsl_type *field_type;
> +
> + if (glsl_type_is_struct(type))
> + field_type = glsl_get_struct_field(type, i);
> + else
> + field_type = glsl_get_array_element(type);
> +
> + int entries = nir_link_uniform(ctx, prog, stage_program, stage,
> + field_type, location,
> + state);
> + if (entries == -1)
> + return -1;
> +
> + if (location != -1)
> + location += entries;
> + location_count += entries;
> +
> + if (glsl_type_is_struct(type))
> + state->current_type = state->current_type->next_sibling;
> + }
> +
> + state->current_type = old_type;
> +
> + return location_count;
> + } else {
> + /* Create a new uniform storage entry */
> + prog->data->UniformStorage =
> + reralloc(prog->data,
> + prog->data->UniformStorage,
> + struct gl_uniform_storage,
> + prog->data->NumUniformStorage + 1);
> + if (!prog->data->UniformStorage) {
> + linker_error(prog, "Out of memory during linking.\n");
> + return -1;
> + }
> +
> + uniform = &prog->data->UniformStorage[prog->data->NumUniformStorage];
> + prog->data->NumUniformStorage++;
> +
> + /* 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;
> +
> + const struct glsl_type *type_no_array = glsl_without_array(type);
> + if (glsl_type_is_array(type)) {
> + uniform->type = type_no_array;
> + uniform->array_elements = glsl_get_length(type);
> + } else {
> + uniform->type = type;
> + uniform->array_elements = 0;
> + }
> + uniform->active_shader_mask |= 1 << stage;
> +
> + /* Uniform has an explicit location */
> + uniform->remap_location = location;
> +
> + /* @FIXME: the initialization of the following will be done as we
> + * implement support for their specific features, like SSBO, atomics,
> + * etc.
> + */
> + uniform->block_index = -1;
> + uniform->offset = -1;
> + uniform->matrix_stride = -1;
> + uniform->array_stride = -1;
> + uniform->row_major = false;
> + uniform->hidden = false;
> + uniform->builtin = false;
> + uniform->is_shader_storage = false;
> + uniform->atomic_buffer_index = -1;
> + uniform->top_level_array_size = 0;
> + uniform->top_level_array_stride = 0;
> + uniform->is_bindless = false;
> +
> + /* The following are not for features not supported by ARB_gl_spirv */
> + uniform->num_compatible_subroutines = 0;
> +
> + unsigned entries = MAX2(1, uniform->array_elements);
> +
> + if (glsl_type_is_sampler(type_no_array)) {
> + int sampler_index =
> + get_next_index(state, uniform, &state->next_sampler_index);
> +
> + state->num_shader_samplers++;
> +
> + uniform->opaque[stage].active = true;
> + uniform->opaque[stage].index = sampler_index;
> +
> + 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++) {
> + stage_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)) {
> + /* @FIXME: image_index should match that of the same image
> + * uniform in other shaders. This means we need to match image
> + * uniforms by location (GLSL does it by variable name, but we
> + * want to avoid that).
> + */
> + int image_index = state->next_image_index;
> + state->next_image_index += entries;
> +
> + state->num_shader_images++;
> +
> + uniform->opaque[stage].active = true;
> + uniform->opaque[stage].index = image_index;
> +
> + /* Set image access qualifiers */
> + const GLenum access =
> + (state->current_var->data.image.read_only ? GL_READ_ONLY :
> + state->current_var->data.image.write_only ? GL_WRITE_ONLY :
> + GL_READ_WRITE);
> + for (unsigned i = image_index;
> + i < MIN2(state->next_image_index, MAX_IMAGE_UNIFORMS);
> + i++) {
> + stage_program->sh.ImageAccess[i] = access;
> + }
> + }
> +
> + unsigned values = glsl_get_component_slots(type);
> + state->num_shader_uniform_components += values;
> + state->num_values += values;
> +
> + if (state->max_uniform_location < uniform->remap_location + entries)
> + state->max_uniform_location = uniform->remap_location + entries;
> +
> + return MAX2(uniform->array_elements, 1);
> + }
> +}
> +
> +bool
> +nir_link_uniforms(struct gl_context *ctx,
> + struct gl_shader_program *prog)
> +{
> + /* First free up any previous UniformStorage items */
> + ralloc_free(prog->data->UniformStorage);
> + prog->data->UniformStorage = NULL;
> + prog->data->NumUniformStorage = 0;
> +
> + /* Iterate through all linked shaders */
> + struct nir_link_uniforms_state state = {0,};
> +
> + for (unsigned shader_type = 0; shader_type < MESA_SHADER_STAGES; shader_type++) {
> + struct gl_linked_shader *sh = prog->_LinkedShaders[shader_type];
> + if (!sh)
> + continue;
> +
> + nir_shader *nir = sh->Program->nir;
> + assert(nir);
> +
> + state.num_shader_samplers = 0;
> + state.num_shader_images = 0;
> + state.num_shader_uniform_components = 0;
> + state.shader_samplers_used = 0;
> + state.shader_shadow_samplers = 0;
> +
> + nir_foreach_variable(var, &nir->uniforms) {
> + struct gl_uniform_storage *uniform = NULL;
> +
> + /* 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_previous_uniform_storage(prog, var->data.location);
> + if (uniform) {
> + uniform->active_shader_mask |= 1 << shader_type;
> + var->data.location = uniform - prog->data->UniformStorage;
> +
> + 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.current_var = var;
> +
> + struct type_tree_entry *type_tree =
> + build_type_tree_for_type(var->type);
> + state.current_type = type_tree;
> +
> + int res = nir_link_uniform(ctx, prog, sh->Program, shader_type, var->type,
> + location, &state);
> +
> + free_type_tree(type_tree);
> +
> + if (res == -1)
> + return false;
> + }
> +
> + sh->Program->SamplersUsed = state.shader_samplers_used;
> + sh->shadow_samplers = state.shader_shadow_samplers;
> + sh->Program->info.num_textures = state.num_shader_samplers;
> + sh->Program->info.num_images = state.num_shader_images;
> + sh->num_uniform_components = state.num_shader_uniform_components;
> + sh->num_combined_uniform_components = sh->num_uniform_components;
> + }
> +
> + prog->data->NumHiddenUniforms = state.num_hidden_uniforms;
> + prog->NumUniformRemapTable = state.max_uniform_location;
> + prog->data->NumUniformDataSlots = state.num_values;
> +
> + nir_setup_uniform_remap_tables(ctx, prog);
> +
> + return true;
> +}
> diff --git a/src/compiler/nir/nir_linker.h b/src/compiler/nir/nir_linker.h
> new file mode 100644
> index 00000000000..d57bed64f97
> --- /dev/null
> +++ b/src/compiler/nir/nir_linker.h
> @@ -0,0 +1,41 @@
> +/*
> + * Copyright © 2017 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#ifndef NIR_LINKER_H
> +#define NIR_LINKER_H
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +struct gl_context;
> +struct gl_shader_program;
> +
> +bool nir_link_uniforms(struct gl_context *ctx,
> + struct gl_shader_program *prog);
> +
> +#ifdef __cplusplus
> +} /* extern "C" */
> +#endif
> +
> +#endif /* NIR_LINKER_H */
>
More information about the mesa-dev
mailing list