1889 lines
70 KiB
C
1889 lines
70 KiB
C
/*
|
||
* 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_gl_types.h"
|
||
#include "nir_deref.h"
|
||
#include "gl_nir_linker.h"
|
||
#include "compiler/glsl/ir_uniform.h" /* for gl_uniform_storage */
|
||
#include "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
|
||
*/
|
||
|
||
#define UNMAPPED_UNIFORM_LOC ~0u
|
||
|
||
struct uniform_array_info {
|
||
/** List of dereferences of the uniform array. */
|
||
struct util_dynarray *deref_list;
|
||
|
||
/** Set of bit-flags to note which array elements have been accessed. */
|
||
BITSET_WORD *indices;
|
||
};
|
||
|
||
/**
|
||
* Built-in / reserved GL variables names start with "gl_"
|
||
*/
|
||
static inline bool
|
||
is_gl_identifier(const char *s)
|
||
{
|
||
return s && s[0] == 'g' && s[1] == 'l' && s[2] == '_';
|
||
}
|
||
|
||
static unsigned
|
||
uniform_storage_size(const struct glsl_type *type)
|
||
{
|
||
switch (glsl_get_base_type(type)) {
|
||
case GLSL_TYPE_STRUCT:
|
||
case GLSL_TYPE_INTERFACE: {
|
||
unsigned size = 0;
|
||
for (unsigned i = 0; i < glsl_get_length(type); i++)
|
||
size += uniform_storage_size(glsl_get_struct_field(type, i));
|
||
return size;
|
||
}
|
||
case GLSL_TYPE_ARRAY: {
|
||
const struct glsl_type *e_type = glsl_get_array_element(type);
|
||
enum glsl_base_type e_base_type = glsl_get_base_type(e_type);
|
||
if (e_base_type == GLSL_TYPE_STRUCT ||
|
||
e_base_type == GLSL_TYPE_INTERFACE ||
|
||
e_base_type == GLSL_TYPE_ARRAY) {
|
||
unsigned length = !glsl_type_is_unsized_array(type) ?
|
||
glsl_get_length(type) : 1;
|
||
return length * uniform_storage_size(e_type);
|
||
} else
|
||
return 1;
|
||
}
|
||
default:
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Update the sizes of linked shader uniform arrays to the maximum
|
||
* array index used.
|
||
*
|
||
* From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec:
|
||
*
|
||
* If one or more elements of an array are active,
|
||
* GetActiveUniform will return the name of the array in name,
|
||
* subject to the restrictions listed above. The type of the array
|
||
* is returned in type. The size parameter contains the highest
|
||
* array element index used, plus one. The compiler or linker
|
||
* determines the highest index used. There will be only one
|
||
* active uniform reported by the GL per uniform array.
|
||
*/
|
||
static void
|
||
update_array_sizes(struct gl_shader_program *prog, nir_variable *var,
|
||
struct hash_table **referenced_uniforms,
|
||
unsigned current_var_stage)
|
||
{
|
||
/* For now we only resize 1D arrays.
|
||
* TODO: add support for resizing more complex array types ??
|
||
*/
|
||
if (!glsl_type_is_array(var->type) ||
|
||
glsl_type_is_array(glsl_get_array_element(var->type)))
|
||
return;
|
||
|
||
/* GL_ARB_uniform_buffer_object says that std140 uniforms
|
||
* will not be eliminated. Since we always do std140, just
|
||
* don't resize arrays in UBOs.
|
||
*
|
||
* Atomic counters are supposed to get deterministic
|
||
* locations assigned based on the declaration ordering and
|
||
* sizes, array compaction would mess that up.
|
||
*
|
||
* Subroutine uniforms are not removed.
|
||
*/
|
||
if (nir_variable_is_in_block(var) || glsl_contains_atomic(var->type) ||
|
||
glsl_get_base_type(glsl_without_array(var->type)) == GLSL_TYPE_SUBROUTINE ||
|
||
var->constant_initializer)
|
||
return;
|
||
|
||
struct uniform_array_info *ainfo = NULL;
|
||
int words = BITSET_WORDS(glsl_array_size(var->type));
|
||
int max_array_size = 0;
|
||
for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
if (!sh)
|
||
continue;
|
||
|
||
struct hash_entry *entry =
|
||
_mesa_hash_table_search(referenced_uniforms[stage], var->name);
|
||
if (entry) {
|
||
ainfo = (struct uniform_array_info *) entry->data;
|
||
max_array_size = MAX2(BITSET_LAST_BIT_SIZED(ainfo->indices, words),
|
||
max_array_size);
|
||
}
|
||
|
||
if (max_array_size == glsl_array_size(var->type))
|
||
return;
|
||
}
|
||
|
||
if (max_array_size != glsl_array_size(var->type)) {
|
||
/* If this is a built-in uniform (i.e., it's backed by some
|
||
* fixed-function state), adjust the number of state slots to
|
||
* match the new array size. The number of slots per array entry
|
||
* is not known. It seems safe to assume that the total number of
|
||
* slots is an integer multiple of the number of array elements.
|
||
* Determine the number of slots per array element by dividing by
|
||
* the old (total) size.
|
||
*/
|
||
const unsigned num_slots = var->num_state_slots;
|
||
if (num_slots > 0) {
|
||
var->num_state_slots =
|
||
(max_array_size * (num_slots / glsl_array_size(var->type)));
|
||
}
|
||
|
||
var->type = glsl_array_type(glsl_get_array_element(var->type),
|
||
max_array_size, 0);
|
||
|
||
/* Update the types of dereferences in case we changed any. */
|
||
struct hash_entry *entry =
|
||
_mesa_hash_table_search(referenced_uniforms[current_var_stage], var->name);
|
||
if (entry) {
|
||
struct uniform_array_info *ainfo =
|
||
(struct uniform_array_info *) entry->data;
|
||
util_dynarray_foreach(ainfo->deref_list, nir_deref_instr *, deref) {
|
||
(*deref)->type = var->type;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
nir_setup_uniform_remap_tables(const struct gl_constants *consts,
|
||
struct gl_shader_program *prog)
|
||
{
|
||
unsigned total_entries = prog->NumExplicitUniformLocations;
|
||
|
||
/* For glsl this may have been allocated by reserve_explicit_locations() so
|
||
* that we can keep track of unused uniforms with explicit locations.
|
||
*/
|
||
assert(!prog->data->spirv ||
|
||
(prog->data->spirv && !prog->UniformRemapTable));
|
||
if (!prog->UniformRemapTable) {
|
||
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;
|
||
|
||
prog->data->UniformDataDefaults =
|
||
rzalloc_array(prog->data->UniformDataSlots,
|
||
union gl_constant_value, prog->data->NumUniformDataSlots);
|
||
|
||
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];
|
||
|
||
if (uniform->is_shader_storage ||
|
||
glsl_get_base_type(uniform->type) == GLSL_TYPE_SUBROUTINE)
|
||
continue;
|
||
|
||
if (prog->data->UniformStorage[i].remap_location == UNMAPPED_UNIFORM_LOC)
|
||
continue;
|
||
|
||
/* 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;
|
||
}
|
||
}
|
||
|
||
/* Reserve locations for rest of the uniforms. */
|
||
if (prog->data->spirv)
|
||
link_util_update_empty_uniform_locations(prog);
|
||
|
||
for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
|
||
struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
|
||
|
||
if (uniform->is_shader_storage ||
|
||
glsl_get_base_type(uniform->type) == GLSL_TYPE_SUBROUTINE)
|
||
continue;
|
||
|
||
/* Built-in uniforms should not get any location. */
|
||
if (uniform->builtin)
|
||
continue;
|
||
|
||
/* Explicit ones have been set already. */
|
||
if (uniform->remap_location != UNMAPPED_UNIFORM_LOC)
|
||
continue;
|
||
|
||
/* How many entries for this uniform? */
|
||
const unsigned entries = MAX2(1, uniform->array_elements);
|
||
|
||
/* Add new entries to the total amount for checking against MAX_UNIFORM-
|
||
* _LOCATIONS. This only applies to the default uniform block (-1),
|
||
* because locations of uniform block entries are not assignable.
|
||
*/
|
||
if (prog->data->UniformStorage[i].block_index == -1)
|
||
total_entries += entries;
|
||
|
||
unsigned location =
|
||
link_util_find_empty_block(prog, &prog->data->UniformStorage[i]);
|
||
|
||
if (location == -1) {
|
||
location = prog->NumUniformRemapTable;
|
||
|
||
/* resize remap table to fit new entries */
|
||
prog->UniformRemapTable =
|
||
reralloc(prog,
|
||
prog->UniformRemapTable,
|
||
struct gl_uniform_storage *,
|
||
prog->NumUniformRemapTable + entries);
|
||
prog->NumUniformRemapTable += entries;
|
||
}
|
||
|
||
/* set the base location in remap table for the uniform */
|
||
uniform->remap_location = location;
|
||
|
||
unsigned num_slots = glsl_get_component_slots(uniform->type);
|
||
|
||
if (uniform->block_index == -1)
|
||
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;
|
||
|
||
if (uniform->block_index == -1)
|
||
data_pos += num_slots;
|
||
}
|
||
}
|
||
|
||
/* Verify that total amount of entries for explicit and implicit locations
|
||
* is less than MAX_UNIFORM_LOCATIONS.
|
||
*/
|
||
if (total_entries > consts->MaxUserAssignableUniformLocations) {
|
||
linker_error(prog, "count of uniform locations > MAX_UNIFORM_LOCATIONS"
|
||
"(%u > %u)", total_entries,
|
||
consts->MaxUserAssignableUniformLocations);
|
||
}
|
||
|
||
/* Reserve all the explicit locations of the active subroutine uniforms. */
|
||
for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
|
||
struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
|
||
|
||
if (glsl_get_base_type(uniform->type) != GLSL_TYPE_SUBROUTINE)
|
||
continue;
|
||
|
||
if (prog->data->UniformStorage[i].remap_location == UNMAPPED_UNIFORM_LOC)
|
||
continue;
|
||
|
||
/* How many new entries for this uniform? */
|
||
const unsigned entries =
|
||
MAX2(1, prog->data->UniformStorage[i].array_elements);
|
||
|
||
uniform->storage = &data[data_pos];
|
||
|
||
unsigned num_slots = glsl_get_component_slots(uniform->type);
|
||
unsigned mask = prog->data->linked_stages;
|
||
while (mask) {
|
||
const int j = u_bit_scan(&mask);
|
||
struct gl_program *p = prog->_LinkedShaders[j]->Program;
|
||
|
||
if (!prog->data->UniformStorage[i].opaque[j].active)
|
||
continue;
|
||
|
||
/* Set remap table entries point to correct gl_uniform_storage. */
|
||
for (unsigned k = 0; k < entries; k++) {
|
||
unsigned element_loc =
|
||
prog->data->UniformStorage[i].remap_location + k;
|
||
p->sh.SubroutineUniformRemapTable[element_loc] =
|
||
&prog->data->UniformStorage[i];
|
||
|
||
data_pos += num_slots;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* reserve subroutine locations */
|
||
for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
|
||
struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
|
||
|
||
if (glsl_get_base_type(uniform->type) != GLSL_TYPE_SUBROUTINE)
|
||
continue;
|
||
|
||
if (prog->data->UniformStorage[i].remap_location !=
|
||
UNMAPPED_UNIFORM_LOC)
|
||
continue;
|
||
|
||
const unsigned entries =
|
||
MAX2(1, prog->data->UniformStorage[i].array_elements);
|
||
|
||
uniform->storage = &data[data_pos];
|
||
|
||
unsigned num_slots = glsl_get_component_slots(uniform->type);
|
||
unsigned mask = prog->data->linked_stages;
|
||
while (mask) {
|
||
const int j = u_bit_scan(&mask);
|
||
struct gl_program *p = prog->_LinkedShaders[j]->Program;
|
||
|
||
if (!prog->data->UniformStorage[i].opaque[j].active)
|
||
continue;
|
||
|
||
p->sh.SubroutineUniformRemapTable =
|
||
reralloc(p,
|
||
p->sh.SubroutineUniformRemapTable,
|
||
struct gl_uniform_storage *,
|
||
p->sh.NumSubroutineUniformRemapTable + entries);
|
||
|
||
for (unsigned k = 0; k < entries; k++) {
|
||
p->sh.SubroutineUniformRemapTable[p->sh.NumSubroutineUniformRemapTable + k] =
|
||
&prog->data->UniformStorage[i];
|
||
|
||
data_pos += num_slots;
|
||
}
|
||
prog->data->UniformStorage[i].remap_location =
|
||
p->sh.NumSubroutineUniformRemapTable;
|
||
p->sh.NumSubroutineUniformRemapTable += entries;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
add_var_use_deref(nir_deref_instr *deref, struct hash_table *live,
|
||
struct array_deref_range **derefs, unsigned *derefs_size)
|
||
{
|
||
nir_deref_path path;
|
||
nir_deref_path_init(&path, deref, NULL);
|
||
|
||
deref = path.path[0];
|
||
if (deref->deref_type != nir_deref_type_var ||
|
||
!nir_deref_mode_is_one_of(deref, nir_var_uniform |
|
||
nir_var_mem_ubo |
|
||
nir_var_mem_ssbo |
|
||
nir_var_image)) {
|
||
nir_deref_path_finish(&path);
|
||
return;
|
||
}
|
||
|
||
/* Number of derefs used in current processing. */
|
||
unsigned num_derefs = 0;
|
||
|
||
const struct glsl_type *deref_type = deref->var->type;
|
||
nir_deref_instr **p = &path.path[1];
|
||
for (; *p; p++) {
|
||
if ((*p)->deref_type == nir_deref_type_array) {
|
||
|
||
/* Skip matrix derefences */
|
||
if (!glsl_type_is_array(deref_type))
|
||
break;
|
||
|
||
if ((num_derefs + 1) * sizeof(struct array_deref_range) > *derefs_size) {
|
||
void *ptr = reralloc_size(NULL, *derefs, *derefs_size + 4096);
|
||
|
||
if (ptr == NULL) {
|
||
nir_deref_path_finish(&path);
|
||
return;
|
||
}
|
||
|
||
*derefs_size += 4096;
|
||
*derefs = (struct array_deref_range *)ptr;
|
||
}
|
||
|
||
struct array_deref_range *dr = &(*derefs)[num_derefs];
|
||
num_derefs++;
|
||
|
||
dr->size = glsl_get_length(deref_type);
|
||
|
||
if (nir_src_is_const((*p)->arr.index)) {
|
||
dr->index = nir_src_as_uint((*p)->arr.index);
|
||
} else {
|
||
/* An unsized array can occur at the end of an SSBO. We can't track
|
||
* accesses to such an array, so bail.
|
||
*/
|
||
if (dr->size == 0) {
|
||
nir_deref_path_finish(&path);
|
||
return;
|
||
}
|
||
|
||
dr->index = dr->size;
|
||
}
|
||
|
||
deref_type = glsl_get_array_element(deref_type);
|
||
} else if ((*p)->deref_type == nir_deref_type_struct) {
|
||
/* We have reached the end of the array. */
|
||
break;
|
||
}
|
||
}
|
||
|
||
nir_deref_path_finish(&path);
|
||
|
||
|
||
struct uniform_array_info *ainfo = NULL;
|
||
|
||
struct hash_entry *entry =
|
||
_mesa_hash_table_search(live, deref->var->name);
|
||
if (!entry && glsl_type_is_array(deref->var->type)) {
|
||
ainfo = ralloc(live, struct uniform_array_info);
|
||
|
||
unsigned num_bits = MAX2(1, glsl_get_aoa_size(deref->var->type));
|
||
ainfo->indices = rzalloc_array(live, BITSET_WORD, BITSET_WORDS(num_bits));
|
||
|
||
ainfo->deref_list = ralloc(live, struct util_dynarray);
|
||
util_dynarray_init(ainfo->deref_list, live);
|
||
}
|
||
|
||
if (entry)
|
||
ainfo = (struct uniform_array_info *) entry->data;
|
||
|
||
if (glsl_type_is_array(deref->var->type)) {
|
||
/* Count the "depth" of the arrays-of-arrays. */
|
||
unsigned array_depth = 0;
|
||
for (const struct glsl_type *type = deref->var->type;
|
||
glsl_type_is_array(type);
|
||
type = glsl_get_array_element(type)) {
|
||
array_depth++;
|
||
}
|
||
|
||
link_util_mark_array_elements_referenced(*derefs, num_derefs, array_depth,
|
||
ainfo->indices);
|
||
|
||
util_dynarray_append(ainfo->deref_list, nir_deref_instr *, deref);
|
||
}
|
||
|
||
assert(deref->modes == deref->var->data.mode);
|
||
_mesa_hash_table_insert(live, deref->var->name, ainfo);
|
||
}
|
||
|
||
/* Iterate over the shader and collect infomation about uniform use */
|
||
static void
|
||
add_var_use_shader(nir_shader *shader, struct hash_table *live)
|
||
{
|
||
/* Currently allocated buffer block of derefs. */
|
||
struct array_deref_range *derefs = NULL;
|
||
|
||
/* Size of the derefs buffer in bytes. */
|
||
unsigned derefs_size = 0;
|
||
|
||
nir_foreach_function(function, shader) {
|
||
if (function->impl) {
|
||
nir_foreach_block(block, function->impl) {
|
||
nir_foreach_instr(instr, block) {
|
||
if (instr->type == nir_instr_type_intrinsic) {
|
||
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
|
||
switch (intr->intrinsic) {
|
||
case nir_intrinsic_atomic_counter_read_deref:
|
||
case nir_intrinsic_atomic_counter_inc_deref:
|
||
case nir_intrinsic_atomic_counter_pre_dec_deref:
|
||
case nir_intrinsic_atomic_counter_post_dec_deref:
|
||
case nir_intrinsic_atomic_counter_add_deref:
|
||
case nir_intrinsic_atomic_counter_min_deref:
|
||
case nir_intrinsic_atomic_counter_max_deref:
|
||
case nir_intrinsic_atomic_counter_and_deref:
|
||
case nir_intrinsic_atomic_counter_or_deref:
|
||
case nir_intrinsic_atomic_counter_xor_deref:
|
||
case nir_intrinsic_atomic_counter_exchange_deref:
|
||
case nir_intrinsic_atomic_counter_comp_swap_deref:
|
||
case nir_intrinsic_image_deref_load:
|
||
case nir_intrinsic_image_deref_store:
|
||
case nir_intrinsic_image_deref_atomic_add:
|
||
case nir_intrinsic_image_deref_atomic_umin:
|
||
case nir_intrinsic_image_deref_atomic_imin:
|
||
case nir_intrinsic_image_deref_atomic_umax:
|
||
case nir_intrinsic_image_deref_atomic_imax:
|
||
case nir_intrinsic_image_deref_atomic_and:
|
||
case nir_intrinsic_image_deref_atomic_or:
|
||
case nir_intrinsic_image_deref_atomic_xor:
|
||
case nir_intrinsic_image_deref_atomic_exchange:
|
||
case nir_intrinsic_image_deref_atomic_comp_swap:
|
||
case nir_intrinsic_image_deref_size:
|
||
case nir_intrinsic_image_deref_samples:
|
||
case nir_intrinsic_load_deref:
|
||
case nir_intrinsic_store_deref:
|
||
add_var_use_deref(nir_src_as_deref(intr->src[0]), live,
|
||
&derefs, &derefs_size);
|
||
break;
|
||
|
||
default:
|
||
/* Nothing to do */
|
||
break;
|
||
}
|
||
} else if (instr->type == nir_instr_type_tex) {
|
||
nir_tex_instr *tex_instr = nir_instr_as_tex(instr);
|
||
int sampler_idx =
|
||
nir_tex_instr_src_index(tex_instr,
|
||
nir_tex_src_sampler_deref);
|
||
int texture_idx =
|
||
nir_tex_instr_src_index(tex_instr,
|
||
nir_tex_src_texture_deref);
|
||
|
||
if (sampler_idx >= 0) {
|
||
nir_deref_instr *deref =
|
||
nir_src_as_deref(tex_instr->src[sampler_idx].src);
|
||
add_var_use_deref(deref, live, &derefs, &derefs_size);
|
||
}
|
||
|
||
if (texture_idx >= 0) {
|
||
nir_deref_instr *deref =
|
||
nir_src_as_deref(tex_instr->src[texture_idx].src);
|
||
add_var_use_deref(deref, live, &derefs, &derefs_size);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
ralloc_free(derefs);
|
||
}
|
||
|
||
static void
|
||
mark_stage_as_active(struct gl_uniform_storage *uniform,
|
||
unsigned stage)
|
||
{
|
||
uniform->active_shader_mask |= 1 << stage;
|
||
}
|
||
|
||
/* 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;
|
||
|
||
/* per-shader stage */
|
||
unsigned next_bindless_image_index;
|
||
unsigned next_bindless_sampler_index;
|
||
unsigned next_image_index;
|
||
unsigned next_sampler_index;
|
||
unsigned next_subroutine;
|
||
unsigned num_shader_samplers;
|
||
unsigned num_shader_images;
|
||
unsigned num_shader_uniform_components;
|
||
unsigned shader_samplers_used;
|
||
unsigned shader_shadow_samplers;
|
||
unsigned shader_storage_blocks_write_access;
|
||
struct gl_program_parameter_list *params;
|
||
|
||
/* per-variable */
|
||
nir_variable *current_var;
|
||
const struct glsl_type *current_ifc_type;
|
||
int offset;
|
||
bool var_is_in_block;
|
||
bool set_top_level_array;
|
||
int top_level_array_size;
|
||
int top_level_array_stride;
|
||
|
||
struct type_tree_entry *current_type;
|
||
struct hash_table *referenced_uniforms[MESA_SHADER_STAGES];
|
||
struct hash_table *uniform_hash;
|
||
};
|
||
|
||
static void
|
||
add_parameter(struct gl_uniform_storage *uniform,
|
||
const struct gl_constants *consts,
|
||
struct gl_shader_program *prog,
|
||
const struct glsl_type *type,
|
||
struct nir_link_uniforms_state *state)
|
||
{
|
||
/* Builtin uniforms are backed by PROGRAM_STATE_VAR, so don't add them as
|
||
* uniforms.
|
||
*/
|
||
if (uniform->builtin)
|
||
return;
|
||
|
||
if (!state->params || uniform->is_shader_storage ||
|
||
(glsl_contains_opaque(type) && !state->current_var->data.bindless))
|
||
return;
|
||
|
||
unsigned num_params = glsl_get_aoa_size(type);
|
||
num_params = MAX2(num_params, 1);
|
||
num_params *= glsl_get_matrix_columns(glsl_without_array(type));
|
||
|
||
bool is_dual_slot = glsl_type_is_dual_slot(glsl_without_array(type));
|
||
if (is_dual_slot)
|
||
num_params *= 2;
|
||
|
||
struct gl_program_parameter_list *params = state->params;
|
||
int base_index = params->NumParameters;
|
||
_mesa_reserve_parameter_storage(params, num_params, num_params);
|
||
|
||
if (consts->PackedDriverUniformStorage) {
|
||
for (unsigned i = 0; i < num_params; i++) {
|
||
unsigned dmul = glsl_type_is_64bit(glsl_without_array(type)) ? 2 : 1;
|
||
unsigned comps = glsl_get_vector_elements(glsl_without_array(type)) * dmul;
|
||
if (is_dual_slot) {
|
||
if (i & 0x1)
|
||
comps -= 4;
|
||
else
|
||
comps = 4;
|
||
}
|
||
|
||
/* TODO: This will waste space with 1 and 3 16-bit components. */
|
||
if (glsl_type_is_16bit(glsl_without_array(type)))
|
||
comps = DIV_ROUND_UP(comps, 2);
|
||
|
||
_mesa_add_parameter(params, PROGRAM_UNIFORM, uniform->name.string, comps,
|
||
glsl_get_gl_type(type), NULL, NULL, false);
|
||
}
|
||
} else {
|
||
for (unsigned i = 0; i < num_params; i++) {
|
||
_mesa_add_parameter(params, PROGRAM_UNIFORM, uniform->name.string, 4,
|
||
glsl_get_gl_type(type), NULL, NULL, true);
|
||
}
|
||
}
|
||
|
||
/* Each Parameter will hold the index to the backing uniform storage.
|
||
* This avoids relying on names to match parameters and uniform
|
||
* storages.
|
||
*/
|
||
for (unsigned i = 0; i < num_params; i++) {
|
||
struct gl_program_parameter *param = ¶ms->Parameters[base_index + i];
|
||
param->UniformStorageIndex = uniform - prog->data->UniformStorage;
|
||
param->MainUniformStorageIndex = state->current_var->data.location;
|
||
}
|
||
}
|
||
|
||
static unsigned
|
||
get_next_index(struct nir_link_uniforms_state *state,
|
||
const struct gl_uniform_storage *uniform,
|
||
unsigned *next_index, bool *initialised)
|
||
{
|
||
/* 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;
|
||
*initialised = true;
|
||
} else
|
||
*initialised = false;
|
||
|
||
unsigned index = state->current_type->next_index;
|
||
|
||
state->current_type->next_index += MAX2(1, uniform->array_elements);
|
||
|
||
return index;
|
||
}
|
||
|
||
/* Update the uniforms info for the current shader stage */
|
||
static void
|
||
update_uniforms_shader_info(struct gl_shader_program *prog,
|
||
struct nir_link_uniforms_state *state,
|
||
struct gl_uniform_storage *uniform,
|
||
const struct glsl_type *type,
|
||
unsigned stage)
|
||
{
|
||
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)) {
|
||
bool init_idx;
|
||
/* ARB_bindless_texture spec says:
|
||
*
|
||
* "When used as shader inputs, outputs, uniform block members,
|
||
* or temporaries, the value of the sampler is a 64-bit unsigned
|
||
* integer handle and never refers to a texture image unit."
|
||
*/
|
||
bool is_bindless = state->current_var->data.bindless || state->var_is_in_block;
|
||
unsigned *next_index = is_bindless ?
|
||
&state->next_bindless_sampler_index :
|
||
&state->next_sampler_index;
|
||
int sampler_index = get_next_index(state, uniform, next_index, &init_idx);
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
|
||
if (is_bindless) {
|
||
if (init_idx) {
|
||
sh->Program->sh.BindlessSamplers =
|
||
rerzalloc(sh->Program, sh->Program->sh.BindlessSamplers,
|
||
struct gl_bindless_sampler,
|
||
sh->Program->sh.NumBindlessSamplers,
|
||
state->next_bindless_sampler_index);
|
||
|
||
for (unsigned j = sh->Program->sh.NumBindlessSamplers;
|
||
j < state->next_bindless_sampler_index; j++) {
|
||
sh->Program->sh.BindlessSamplers[j].target =
|
||
glsl_get_sampler_target(type_no_array);
|
||
}
|
||
|
||
sh->Program->sh.NumBindlessSamplers =
|
||
state->next_bindless_sampler_index;
|
||
}
|
||
|
||
if (!state->var_is_in_block)
|
||
state->num_shader_uniform_components += values;
|
||
} else {
|
||
/* Samplers (bound or bindless) are counted as two components
|
||
* as specified by ARB_bindless_texture.
|
||
*/
|
||
state->num_shader_samplers += values / 2;
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
uniform->opaque[stage].active = true;
|
||
uniform->opaque[stage].index = sampler_index;
|
||
} else if (glsl_type_is_image(type_no_array)) {
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
|
||
/* 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);
|
||
|
||
int image_index;
|
||
if (state->current_var->data.bindless) {
|
||
image_index = state->next_bindless_image_index;
|
||
state->next_bindless_image_index += MAX2(1, uniform->array_elements);
|
||
|
||
sh->Program->sh.BindlessImages =
|
||
rerzalloc(sh->Program, sh->Program->sh.BindlessImages,
|
||
struct gl_bindless_image,
|
||
sh->Program->sh.NumBindlessImages,
|
||
state->next_bindless_image_index);
|
||
|
||
for (unsigned j = sh->Program->sh.NumBindlessImages;
|
||
j < state->next_bindless_image_index; j++) {
|
||
sh->Program->sh.BindlessImages[j].access = access;
|
||
}
|
||
|
||
sh->Program->sh.NumBindlessImages = state->next_bindless_image_index;
|
||
|
||
} else {
|
||
image_index = state->next_image_index;
|
||
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;
|
||
|
||
for (unsigned i = image_index;
|
||
i < MIN2(state->next_image_index, MAX_IMAGE_UNIFORMS); i++) {
|
||
sh->Program->sh.ImageAccess[i] = access;
|
||
}
|
||
}
|
||
|
||
uniform->opaque[stage].active = true;
|
||
uniform->opaque[stage].index = image_index;
|
||
|
||
if (!uniform->is_shader_storage)
|
||
state->num_shader_uniform_components += values;
|
||
} else {
|
||
if (glsl_get_base_type(type_no_array) == GLSL_TYPE_SUBROUTINE) {
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
|
||
uniform->opaque[stage].index = state->next_subroutine;
|
||
uniform->opaque[stage].active = true;
|
||
|
||
sh->Program->sh.NumSubroutineUniforms++;
|
||
|
||
/* Increment the subroutine index by 1 for non-arrays and by the
|
||
* number of array elements for arrays.
|
||
*/
|
||
state->next_subroutine += MAX2(1, uniform->array_elements);
|
||
}
|
||
|
||
if (!state->var_is_in_block)
|
||
state->num_shader_uniform_components += values;
|
||
}
|
||
}
|
||
|
||
static bool
|
||
find_and_update_named_uniform_storage(const struct gl_constants *consts,
|
||
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(consts, 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;
|
||
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;
|
||
}
|
||
|
||
update_uniforms_shader_info(prog, state, uniform, type, stage);
|
||
|
||
const struct glsl_type *type_no_array = glsl_without_array(type);
|
||
struct hash_entry *entry = prog->data->spirv ? NULL :
|
||
_mesa_hash_table_search(state->referenced_uniforms[stage],
|
||
state->current_var->name);
|
||
if (entry != NULL ||
|
||
glsl_get_base_type(type_no_array) == GLSL_TYPE_SUBROUTINE ||
|
||
prog->data->spirv)
|
||
uniform->active_shader_mask |= 1 << stage;
|
||
|
||
if (!state->var_is_in_block)
|
||
add_parameter(uniform, consts, prog, type, state);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Finds, returns, and updates the stage info for any uniform in UniformStorage
|
||
* 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
|
||
* found. In that case all of them are updated, and the first entry is
|
||
* returned, in order to update the location of the nir variable.
|
||
*
|
||
* * Special uniforms: like atomic counters. They lack a explicit location,
|
||
* so they are skipped. They will be handled and assigned a location later.
|
||
*
|
||
*/
|
||
static bool
|
||
find_and_update_previous_uniform_storage(const struct gl_constants *consts,
|
||
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(consts, 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;
|
||
|
||
ASSERTED unsigned num_blks = nir_variable_is_in_ubo(var) ?
|
||
prog->data->NumUniformBlocks :
|
||
prog->data->NumShaderStorageBlocks;
|
||
|
||
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 !=
|
||
nir_variable_is_in_ssbo(var))
|
||
continue;
|
||
|
||
int block_index = prog->data->UniformStorage[i].block_index;
|
||
if (block_index != -1) {
|
||
assert(block_index < num_blks);
|
||
|
||
if (var->data.binding == blks[block_index].Binding) {
|
||
if (!uniform)
|
||
uniform = &prog->data->UniformStorage[i];
|
||
mark_stage_as_active(&prog->data->UniformStorage[i],
|
||
stage);
|
||
result = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (result)
|
||
var->data.location = uniform - prog->data->UniformStorage;
|
||
return result;
|
||
}
|
||
|
||
/* Beyond blocks, there are still some corner cases of uniforms without
|
||
* location (ie: atomic counters) that would have a initial location equal
|
||
* to -1. We just return on that case. Those uniforms will be handled
|
||
* later.
|
||
*/
|
||
if (var->data.location == -1)
|
||
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
|
||
*/
|
||
for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
|
||
if (prog->data->UniformStorage[i].remap_location == var->data.location) {
|
||
mark_stage_as_active(&prog->data->UniformStorage[i], stage);
|
||
|
||
struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
|
||
var->data.location = uniform - prog->data->UniformStorage;
|
||
add_parameter(uniform, consts, prog, var->type, state);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
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_or_ifc(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 void
|
||
hash_free_uniform_name(struct hash_entry *entry)
|
||
{
|
||
free((void*)entry->key);
|
||
}
|
||
|
||
static void
|
||
enter_record(struct nir_link_uniforms_state *state,
|
||
const struct gl_constants *consts,
|
||
const struct glsl_type *type,
|
||
bool row_major)
|
||
{
|
||
assert(glsl_type_is_struct(type));
|
||
if (!state->var_is_in_block)
|
||
return;
|
||
|
||
bool use_std430 = consts->UseSTD430AsDefaultPacking;
|
||
const enum glsl_interface_packing packing =
|
||
glsl_get_internal_ifc_packing(state->current_var->interface_type,
|
||
use_std430);
|
||
|
||
if (packing == GLSL_INTERFACE_PACKING_STD430)
|
||
state->offset = glsl_align(
|
||
state->offset, glsl_get_std430_base_alignment(type, row_major));
|
||
else
|
||
state->offset = glsl_align(
|
||
state->offset, glsl_get_std140_base_alignment(type, row_major));
|
||
}
|
||
|
||
static void
|
||
leave_record(struct nir_link_uniforms_state *state,
|
||
const struct gl_constants *consts,
|
||
const struct glsl_type *type,
|
||
bool row_major)
|
||
{
|
||
assert(glsl_type_is_struct(type));
|
||
if (!state->var_is_in_block)
|
||
return;
|
||
|
||
bool use_std430 = consts->UseSTD430AsDefaultPacking;
|
||
const enum glsl_interface_packing packing =
|
||
glsl_get_internal_ifc_packing(state->current_var->interface_type,
|
||
use_std430);
|
||
|
||
if (packing == GLSL_INTERFACE_PACKING_STD430)
|
||
state->offset = glsl_align(
|
||
state->offset, glsl_get_std430_base_alignment(type, row_major));
|
||
else
|
||
state->offset = glsl_align(
|
||
state->offset, glsl_get_std140_base_alignment(type, row_major));
|
||
}
|
||
|
||
/**
|
||
* Creates the neccessary entries in UniformStorage for the uniform. Returns
|
||
* the number of locations used or -1 on failure.
|
||
*/
|
||
static int
|
||
nir_link_uniform(const struct gl_constants *consts,
|
||
struct gl_shader_program *prog,
|
||
struct gl_program *stage_program,
|
||
gl_shader_stage stage,
|
||
const struct glsl_type *type,
|
||
unsigned index_in_parent,
|
||
int location,
|
||
struct nir_link_uniforms_state *state,
|
||
char **name, size_t name_length, bool row_major)
|
||
{
|
||
struct gl_uniform_storage *uniform = NULL;
|
||
|
||
if (state->set_top_level_array &&
|
||
nir_variable_is_in_ssbo(state->current_var)) {
|
||
/* Type is the top level SSBO member */
|
||
if (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)))) {
|
||
/* Type is a top-level array (array of aggregate types) */
|
||
state->top_level_array_size = glsl_get_length(type);
|
||
state->top_level_array_stride = glsl_get_explicit_stride(type);
|
||
} else {
|
||
state->top_level_array_size = 1;
|
||
state->top_level_array_stride = 0;
|
||
}
|
||
|
||
state->set_top_level_array = false;
|
||
}
|
||
|
||
/* 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))))) {
|
||
int location_count = 0;
|
||
struct type_tree_entry *old_type = state->current_type;
|
||
unsigned int struct_base_offset = state->offset;
|
||
|
||
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;
|
||
|
||
if (glsl_type_is_struct(type) && !prog->data->spirv)
|
||
enter_record(state, consts, type, row_major);
|
||
|
||
for (unsigned i = 0; i < length; i++) {
|
||
const struct glsl_type *field_type;
|
||
size_t new_length = name_length;
|
||
bool field_row_major = row_major;
|
||
|
||
if (glsl_type_is_struct_or_ifc(type)) {
|
||
field_type = glsl_get_struct_field(type, i);
|
||
/* Use the offset inside the struct only for variables backed by
|
||
* a buffer object. For variables not backed by a buffer object,
|
||
* offset is -1.
|
||
*/
|
||
if (state->var_is_in_block) {
|
||
if (prog->data->spirv) {
|
||
state->offset =
|
||
struct_base_offset + glsl_get_struct_field_offset(type, i);
|
||
} else if (glsl_get_struct_field_offset(type, i) != -1 &&
|
||
type == state->current_ifc_type) {
|
||
state->offset = glsl_get_struct_field_offset(type, i);
|
||
}
|
||
|
||
if (glsl_type_is_interface(type))
|
||
state->set_top_level_array = true;
|
||
}
|
||
|
||
/* Append '.field' to the current variable name. */
|
||
if (name) {
|
||
ralloc_asprintf_rewrite_tail(name, &new_length, ".%s",
|
||
glsl_get_struct_elem_name(type, i));
|
||
}
|
||
|
||
|
||
/* The layout of structures at the top level of the block is set
|
||
* during parsing. For matrices contained in multiple levels of
|
||
* structures in the block, the inner structures have no layout.
|
||
* These cases must potentially inherit the layout from the outer
|
||
* levels.
|
||
*/
|
||
const enum glsl_matrix_layout matrix_layout =
|
||
glsl_get_struct_field_data(type, i)->matrix_layout;
|
||
if (matrix_layout == GLSL_MATRIX_LAYOUT_ROW_MAJOR) {
|
||
field_row_major = true;
|
||
} else if (matrix_layout == GLSL_MATRIX_LAYOUT_COLUMN_MAJOR) {
|
||
field_row_major = false;
|
||
}
|
||
} 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(consts, prog, stage_program, stage,
|
||
field_type, i, location,
|
||
state, name, new_length,
|
||
field_row_major);
|
||
|
||
if (entries == -1)
|
||
return -1;
|
||
|
||
if (location != -1)
|
||
location += entries;
|
||
location_count += entries;
|
||
|
||
if (glsl_type_is_struct_or_ifc(type))
|
||
state->current_type = state->current_type->next_sibling;
|
||
}
|
||
|
||
if (glsl_type_is_struct(type) && !prog->data->spirv)
|
||
leave_record(state, consts, type, row_major);
|
||
|
||
state->current_type = old_type;
|
||
|
||
return location_count;
|
||
} else {
|
||
/* TODO: reallocating storage is slow, we should figure out a way to
|
||
* allocate storage up front for spirv like we do for GLSL.
|
||
*/
|
||
if (prog->data->spirv) {
|
||
/* 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));
|
||
|
||
uniform->name.string =
|
||
name ? ralloc_strdup(prog->data->UniformStorage, *name) : NULL;
|
||
resource_name_updated(&uniform->name);
|
||
|
||
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->top_level_array_size = state->top_level_array_size;
|
||
uniform->top_level_array_stride = state->top_level_array_stride;
|
||
|
||
struct hash_entry *entry = prog->data->spirv ? NULL :
|
||
_mesa_hash_table_search(state->referenced_uniforms[stage],
|
||
state->current_var->name);
|
||
if (entry != NULL ||
|
||
glsl_get_base_type(type_no_array) == GLSL_TYPE_SUBROUTINE ||
|
||
prog->data->spirv)
|
||
uniform->active_shader_mask |= 1 << stage;
|
||
|
||
if (location >= 0) {
|
||
/* Uniform has an explicit location */
|
||
uniform->remap_location = location;
|
||
} else {
|
||
uniform->remap_location = UNMAPPED_UNIFORM_LOC;
|
||
}
|
||
|
||
uniform->hidden = state->current_var->data.how_declared == nir_var_hidden;
|
||
if (uniform->hidden)
|
||
state->num_hidden_uniforms++;
|
||
|
||
uniform->is_shader_storage = nir_variable_is_in_ssbo(state->current_var);
|
||
uniform->is_bindless = state->current_var->data.bindless;
|
||
|
||
/* Set fields whose default value depend on the variable being inside a
|
||
* block.
|
||
*
|
||
* From the OpenGL 4.6 spec, 7.3 Program objects:
|
||
*
|
||
* "For the property ARRAY_STRIDE, ... For active variables not declared
|
||
* as an array of basic types, zero is written to params. For active
|
||
* variables not backed by a buffer object, -1 is written to params,
|
||
* regardless of the variable type."
|
||
*
|
||
* "For the property MATRIX_STRIDE, ... For active variables not declared
|
||
* as a matrix or array of matrices, zero is written to params. For active
|
||
* variables not backed by a buffer object, -1 is written to params,
|
||
* regardless of the variable type."
|
||
*
|
||
* For the property IS_ROW_MAJOR, ... For active variables backed by a
|
||
* buffer object, declared as a single matrix or array of matrices, and
|
||
* stored in row-major order, one is written to params. For all other
|
||
* active variables, zero is written to params.
|
||
*/
|
||
uniform->array_stride = -1;
|
||
uniform->matrix_stride = -1;
|
||
uniform->row_major = false;
|
||
|
||
if (state->var_is_in_block) {
|
||
uniform->array_stride = glsl_type_is_array(type) ?
|
||
glsl_get_explicit_stride(type) : 0;
|
||
|
||
if (glsl_type_is_matrix(uniform->type)) {
|
||
uniform->matrix_stride = glsl_get_explicit_stride(uniform->type);
|
||
uniform->row_major = glsl_matrix_type_is_row_major(uniform->type);
|
||
} else {
|
||
uniform->matrix_stride = 0;
|
||
}
|
||
|
||
if (!prog->data->spirv) {
|
||
bool use_std430 = consts->UseSTD430AsDefaultPacking;
|
||
const enum glsl_interface_packing packing =
|
||
glsl_get_internal_ifc_packing(state->current_var->interface_type,
|
||
use_std430);
|
||
|
||
unsigned alignment =
|
||
glsl_get_std140_base_alignment(type, uniform->row_major);
|
||
if (packing == GLSL_INTERFACE_PACKING_STD430) {
|
||
alignment =
|
||
glsl_get_std430_base_alignment(type, uniform->row_major);
|
||
}
|
||
state->offset = glsl_align(state->offset, alignment);
|
||
}
|
||
}
|
||
|
||
uniform->offset = state->var_is_in_block ? state->offset : -1;
|
||
|
||
int buffer_block_index = -1;
|
||
/* If the uniform is inside a uniform block determine its block index by
|
||
* comparing the bindings, we can not use names.
|
||
*/
|
||
if (state->var_is_in_block) {
|
||
struct gl_uniform_block *blocks = nir_variable_is_in_ssbo(state->current_var) ?
|
||
prog->data->ShaderStorageBlocks : prog->data->UniformBlocks;
|
||
|
||
int num_blocks = nir_variable_is_in_ssbo(state->current_var) ?
|
||
prog->data->NumShaderStorageBlocks : prog->data->NumUniformBlocks;
|
||
|
||
if (!prog->data->spirv) {
|
||
bool is_interface_array =
|
||
glsl_without_array(state->current_var->type) == state->current_var->interface_type &&
|
||
glsl_type_is_array(state->current_var->type);
|
||
|
||
const char *ifc_name =
|
||
glsl_get_type_name(state->current_var->interface_type);
|
||
if (is_interface_array) {
|
||
unsigned l = strlen(ifc_name);
|
||
for (unsigned i = 0; i < num_blocks; i++) {
|
||
if (strncmp(ifc_name, blocks[i].name.string, l) == 0 &&
|
||
blocks[i].name.string[l] == '[') {
|
||
buffer_block_index = i;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
for (unsigned i = 0; i < num_blocks; i++) {
|
||
if (strcmp(ifc_name, blocks[i].name.string) == 0) {
|
||
buffer_block_index = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Compute the next offset. */
|
||
bool use_std430 = consts->UseSTD430AsDefaultPacking;
|
||
const enum glsl_interface_packing packing =
|
||
glsl_get_internal_ifc_packing(state->current_var->interface_type,
|
||
use_std430);
|
||
if (packing == GLSL_INTERFACE_PACKING_STD430)
|
||
state->offset += glsl_get_std430_size(type, uniform->row_major);
|
||
else
|
||
state->offset += glsl_get_std140_size(type, uniform->row_major);
|
||
} else {
|
||
for (unsigned i = 0; i < num_blocks; i++) {
|
||
if (state->current_var->data.binding == blocks[i].Binding) {
|
||
buffer_block_index = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Compute the next offset. */
|
||
state->offset += glsl_get_explicit_size(type, true);
|
||
}
|
||
assert(buffer_block_index >= 0);
|
||
}
|
||
|
||
uniform->block_index = buffer_block_index;
|
||
uniform->builtin = is_gl_identifier(uniform->name.string);
|
||
uniform->atomic_buffer_index = -1;
|
||
|
||
/* The following are not for features not supported by ARB_gl_spirv */
|
||
uniform->num_compatible_subroutines = 0;
|
||
|
||
unsigned entries = MAX2(1, uniform->array_elements);
|
||
unsigned values = glsl_get_component_slots(type);
|
||
|
||
update_uniforms_shader_info(prog, state, uniform, type, stage);
|
||
|
||
if (uniform->remap_location != UNMAPPED_UNIFORM_LOC &&
|
||
state->max_uniform_location < uniform->remap_location + entries)
|
||
state->max_uniform_location = uniform->remap_location + entries;
|
||
|
||
if (!state->var_is_in_block)
|
||
add_parameter(uniform, consts, prog, type, state);
|
||
|
||
if (name) {
|
||
_mesa_hash_table_insert(state->uniform_hash, strdup(*name),
|
||
(void *) (intptr_t)
|
||
(prog->data->NumUniformStorage - 1));
|
||
}
|
||
|
||
if (!is_gl_identifier(uniform->name.string) && !uniform->is_shader_storage &&
|
||
!state->var_is_in_block)
|
||
state->num_values += values;
|
||
|
||
return MAX2(uniform->array_elements, 1);
|
||
}
|
||
}
|
||
|
||
bool
|
||
gl_nir_link_uniforms(const struct gl_constants *consts,
|
||
struct gl_shader_program *prog,
|
||
bool fill_parameters)
|
||
{
|
||
/* 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,};
|
||
|
||
if (!prog->data->spirv) {
|
||
/* Gather information on uniform use */
|
||
for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
if (!sh)
|
||
continue;
|
||
|
||
state.referenced_uniforms[stage] =
|
||
_mesa_hash_table_create(NULL, _mesa_hash_string,
|
||
_mesa_key_string_equal);
|
||
|
||
nir_shader *nir = sh->Program->nir;
|
||
add_var_use_shader(nir, state.referenced_uniforms[stage]);
|
||
}
|
||
|
||
/* Resize uniform arrays based on the maximum array index */
|
||
for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
if (!sh)
|
||
continue;
|
||
|
||
nir_foreach_gl_uniform_variable(var, sh->Program->nir)
|
||
update_array_sizes(prog, var, state.referenced_uniforms, stage);
|
||
}
|
||
}
|
||
|
||
/* Count total number of uniforms and allocate storage */
|
||
unsigned storage_size = 0;
|
||
if (!prog->data->spirv) {
|
||
struct set *storage_counted =
|
||
_mesa_set_create(NULL, _mesa_hash_string, _mesa_key_string_equal);
|
||
for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
||
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
||
if (!sh)
|
||
continue;
|
||
|
||
nir_foreach_gl_uniform_variable(var, sh->Program->nir) {
|
||
const struct glsl_type *type = var->type;
|
||
const char *name = var->name;
|
||
if (nir_variable_is_in_block(var) &&
|
||
glsl_without_array(type) == var->interface_type) {
|
||
type = glsl_without_array(var->type);
|
||
name = glsl_get_type_name(type);
|
||
}
|
||
|
||
struct set_entry *entry = _mesa_set_search(storage_counted, name);
|
||
if (!entry) {
|
||
storage_size += uniform_storage_size(type);
|
||
_mesa_set_add(storage_counted, name);
|
||
}
|
||
}
|
||
}
|
||
_mesa_set_destroy(storage_counted, NULL);
|
||
|
||
prog->data->UniformStorage = rzalloc_array(prog->data,
|
||
struct gl_uniform_storage,
|
||
storage_size);
|
||
if (!prog->data->UniformStorage) {
|
||
linker_error(prog, "Out of memory while linking uniforms.\n");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/* Iterate through all linked shaders */
|
||
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];
|
||
if (!sh)
|
||
continue;
|
||
|
||
nir_shader *nir = sh->Program->nir;
|
||
assert(nir);
|
||
|
||
state.next_bindless_image_index = 0;
|
||
state.next_bindless_sampler_index = 0;
|
||
state.next_image_index = 0;
|
||
state.next_sampler_index = 0;
|
||
state.num_shader_samplers = 0;
|
||
state.num_shader_images = 0;
|
||
state.num_shader_uniform_components = 0;
|
||
state.shader_storage_blocks_write_access = 0;
|
||
state.shader_samplers_used = 0;
|
||
state.shader_shadow_samplers = 0;
|
||
state.params = fill_parameters ? sh->Program->Parameters : NULL;
|
||
|
||
nir_foreach_gl_uniform_variable(var, nir) {
|
||
state.current_var = var;
|
||
state.current_ifc_type = NULL;
|
||
state.offset = 0;
|
||
state.var_is_in_block = nir_variable_is_in_block(var);
|
||
state.set_top_level_array = false;
|
||
state.top_level_array_size = 0;
|
||
state.top_level_array_stride = 0;
|
||
|
||
/*
|
||
* From ARB_program_interface spec, issue (16):
|
||
*
|
||
* "RESOLVED: We will follow the default rule for enumerating block
|
||
* members in the OpenGL API, which is:
|
||
*
|
||
* * If a variable is a member of an interface block without an
|
||
* instance name, it is enumerated using just the variable name.
|
||
*
|
||
* * If a variable is a member of an interface block with an
|
||
* instance name, it is enumerated as "BlockName.Member", where
|
||
* "BlockName" is the name of the interface block (not the
|
||
* instance name) and "Member" is the name of the variable.
|
||
*
|
||
* For example, in the following code:
|
||
*
|
||
* uniform Block1 {
|
||
* int member1;
|
||
* };
|
||
* uniform Block2 {
|
||
* int member2;
|
||
* } instance2;
|
||
* uniform Block3 {
|
||
* int member3;
|
||
* } instance3[2]; // uses two separate buffer bindings
|
||
*
|
||
* the three uniforms (if active) are enumerated as "member1",
|
||
* "Block2.member2", and "Block3.member3"."
|
||
*
|
||
* Note that in the last example, with an array of ubo, only one
|
||
* uniform is generated. For that reason, while unrolling the
|
||
* 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 &&
|
||
((!prog->data->spirv && glsl_without_array(type) == var->interface_type) ||
|
||
(prog->data->spirv && type == var->interface_type))) {
|
||
type = glsl_without_array(var->type);
|
||
state.current_ifc_type = type;
|
||
name = ralloc_strdup(NULL, glsl_get_type_name(type));
|
||
} else {
|
||
state.set_top_level_array = true;
|
||
name = ralloc_strdup(NULL, var->name);
|
||
}
|
||
|
||
struct type_tree_entry *type_tree =
|
||
build_type_tree_for_type(type);
|
||
state.current_type = type_tree;
|
||
|
||
int location = var->data.location;
|
||
|
||
struct gl_uniform_block *blocks = NULL;
|
||
int num_blocks = 0;
|
||
int buffer_block_index = -1;
|
||
if (!prog->data->spirv && state.var_is_in_block) {
|
||
/* If the uniform is inside a uniform block determine its block index by
|
||
* comparing the bindings, we can not use names.
|
||
*/
|
||
blocks = nir_variable_is_in_ssbo(state.current_var) ?
|
||
prog->data->ShaderStorageBlocks : prog->data->UniformBlocks;
|
||
num_blocks = nir_variable_is_in_ssbo(state.current_var) ?
|
||
prog->data->NumShaderStorageBlocks : prog->data->NumUniformBlocks;
|
||
|
||
bool is_interface_array =
|
||
glsl_without_array(state.current_var->type) == state.current_var->interface_type &&
|
||
glsl_type_is_array(state.current_var->type);
|
||
|
||
const char *ifc_name =
|
||
glsl_get_type_name(state.current_var->interface_type);
|
||
|
||
if (is_interface_array) {
|
||
unsigned l = strlen(ifc_name);
|
||
|
||
/* Even when a match is found, do not "break" here. As this is
|
||
* an array of instances, all elements of the array need to be
|
||
* marked as referenced.
|
||
*/
|
||
for (unsigned i = 0; i < num_blocks; i++) {
|
||
if (strncmp(ifc_name, blocks[i].name.string, l) == 0 &&
|
||
blocks[i].name.string[l] == '[') {
|
||
if (buffer_block_index == -1)
|
||
buffer_block_index = i;
|
||
|
||
struct hash_entry *entry =
|
||
_mesa_hash_table_search(state.referenced_uniforms[shader_type],
|
||
var->name);
|
||
if (entry) {
|
||
struct uniform_array_info *ainfo =
|
||
(struct uniform_array_info *) entry->data;
|
||
if (BITSET_TEST(ainfo->indices, blocks[i].linearized_array_index))
|
||
blocks[i].stageref |= 1U << shader_type;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
for (unsigned i = 0; i < num_blocks; i++) {
|
||
if (strcmp(ifc_name, blocks[i].name.string) == 0) {
|
||
buffer_block_index = i;
|
||
|
||
struct hash_entry *entry =
|
||
_mesa_hash_table_search(state.referenced_uniforms[shader_type],
|
||
var->name);
|
||
if (entry)
|
||
blocks[i].stageref |= 1U << shader_type;
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (nir_variable_is_in_ssbo(var) &&
|
||
!(var->data.access & ACCESS_NON_WRITEABLE)) {
|
||
unsigned array_size = is_interface_array ?
|
||
glsl_get_length(var->type) : 1;
|
||
|
||
STATIC_ASSERT(MAX_SHADER_STORAGE_BUFFERS <= 32);
|
||
|
||
/* Shaders that use too many SSBOs will fail to compile, which
|
||
* we don't care about.
|
||
*
|
||
* This is true for shaders that do not use too many SSBOs:
|
||
*/
|
||
if (buffer_block_index + array_size <= 32) {
|
||
state.shader_storage_blocks_write_access |=
|
||
u_bit_consecutive(buffer_block_index, array_size);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (blocks && !prog->data->spirv && state.var_is_in_block) {
|
||
if (glsl_without_array(state.current_var->type) != state.current_var->interface_type) {
|
||
/* this is nested at some offset inside the block */
|
||
bool found = false;
|
||
char sentinel = '\0';
|
||
|
||
if (glsl_type_is_struct(state.current_var->type)) {
|
||
sentinel = '.';
|
||
} else if (glsl_type_is_array(state.current_var->type) &&
|
||
(glsl_type_is_array(glsl_get_array_element(state.current_var->type))
|
||
|| glsl_type_is_struct(glsl_without_array(state.current_var->type)))) {
|
||
sentinel = '[';
|
||
}
|
||
|
||
const unsigned l = strlen(state.current_var->name);
|
||
for (unsigned i = 0; i < num_blocks; i++) {
|
||
for (unsigned j = 0; j < blocks[i].NumUniforms; j++) {
|
||
if (sentinel) {
|
||
const char *begin = blocks[i].Uniforms[j].Name;
|
||
const char *end = strchr(begin, sentinel);
|
||
|
||
if (end == NULL)
|
||
continue;
|
||
|
||
if ((ptrdiff_t) l != (end - begin))
|
||
continue;
|
||
found = strncmp(state.current_var->name, begin, l) == 0;
|
||
} else {
|
||
found = strcmp(state.current_var->name, blocks[i].Uniforms[j].Name) == 0;
|
||
}
|
||
|
||
if (found) {
|
||
location = j;
|
||
|
||
struct hash_entry *entry =
|
||
_mesa_hash_table_search(state.referenced_uniforms[shader_type], var->name);
|
||
if (entry)
|
||
blocks[i].stageref |= 1U << shader_type;
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (found)
|
||
break;
|
||
}
|
||
assert(found);
|
||
var->data.location = location;
|
||
} else {
|
||
/* this is the base block offset */
|
||
var->data.location = buffer_block_index;
|
||
location = 0;
|
||
}
|
||
assert(buffer_block_index >= 0);
|
||
const struct gl_uniform_block *const block =
|
||
&blocks[buffer_block_index];
|
||
assert(location >= 0 && location < block->NumUniforms);
|
||
|
||
const struct gl_uniform_buffer_variable *const ubo_var =
|
||
&block->Uniforms[location];
|
||
|
||
state.offset = ubo_var->Offset;
|
||
}
|
||
|
||
/* 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(consts, prog, &state, var,
|
||
name, type, shader_type)) {
|
||
ralloc_free(name);
|
||
free_type_tree(type_tree);
|
||
continue;
|
||
}
|
||
|
||
/* 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;
|
||
|
||
bool row_major =
|
||
var->data.matrix_layout == GLSL_MATRIX_LAYOUT_ROW_MAJOR;
|
||
int res = nir_link_uniform(consts, prog, sh->Program, shader_type, type,
|
||
0, location,
|
||
&state,
|
||
!prog->data->spirv ? &name : NULL,
|
||
!prog->data->spirv ? strlen(name) : 0,
|
||
row_major);
|
||
|
||
free_type_tree(type_tree);
|
||
ralloc_free(name);
|
||
|
||
if (res == -1)
|
||
return false;
|
||
}
|
||
|
||
if (!prog->data->spirv) {
|
||
_mesa_hash_table_destroy(state.referenced_uniforms[shader_type],
|
||
NULL);
|
||
}
|
||
|
||
if (state.num_shader_samplers >
|
||
consts->Program[shader_type].MaxTextureImageUnits) {
|
||
linker_error(prog, "Too many %s shader texture samplers\n",
|
||
_mesa_shader_stage_to_string(shader_type));
|
||
continue;
|
||
}
|
||
|
||
if (state.num_shader_images >
|
||
consts->Program[shader_type].MaxImageUniforms) {
|
||
linker_error(prog, "Too many %s shader image uniforms (%u > %u)\n",
|
||
_mesa_shader_stage_to_string(shader_type),
|
||
state.num_shader_images,
|
||
consts->Program[shader_type].MaxImageUniforms);
|
||
continue;
|
||
}
|
||
|
||
sh->Program->SamplersUsed = state.shader_samplers_used;
|
||
sh->Program->sh.ShaderStorageBlocksWriteAccess =
|
||
state.shader_storage_blocks_write_access;
|
||
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->data->NumUniformDataSlots = state.num_values;
|
||
|
||
assert(prog->data->spirv || prog->data->NumUniformStorage == storage_size);
|
||
|
||
if (prog->data->spirv)
|
||
prog->NumUniformRemapTable = state.max_uniform_location;
|
||
|
||
nir_setup_uniform_remap_tables(consts, prog);
|
||
gl_nir_set_uniform_initializers(consts, prog);
|
||
|
||
_mesa_hash_table_destroy(state.uniform_hash, hash_free_uniform_name);
|
||
|
||
return true;
|
||
}
|