glsl: add gl_nir_linker_size_arrays()

This will size implicitly size arrays during linking, replacing the glsl
ir version.

Acked-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31137>
This commit is contained in:
Timothy Arceri 2024-05-30 13:11:02 +10:00 committed by Marge Bot
parent a235da080e
commit 083b4ffb78
2 changed files with 227 additions and 0 deletions

View file

@ -34,6 +34,231 @@
#include "util/hash_table.h"
#include "util/u_string.h"
/**
* Change var->interface_type on a variable that previously had a
* different, but compatible, interface_type. This is used during linking
* to set the size of arrays in interface blocks.
*/
static void
change_interface_type(nir_variable *var, const struct glsl_type *type)
{
if (var->max_ifc_array_access != NULL) {
/* max_ifc_array_access has already been allocated, so make sure the
* new interface has the same number of fields as the old one.
*/
assert(var->interface_type->length == type->length);
}
var->interface_type = type;
}
/**
* If the type pointed to by \c type represents an unsized array, replace
* it with a sized array whose size is determined by max_array_access.
*/
static void
fixup_type(const struct glsl_type **type, unsigned max_array_access,
bool from_ssbo_unsized_array, bool *implicit_sized)
{
if (!from_ssbo_unsized_array && glsl_type_is_unsized_array(*type)) {
*type = glsl_array_type((*type)->fields.array,
max_array_access + 1, (*type)->explicit_stride);
*implicit_sized = true;
assert(*type != NULL);
}
}
static void
fixup_unnamed_interface_type(const void *key, void *data,
UNUSED void *closure)
{
const struct glsl_type *ifc_type = (const struct glsl_type *) key;
nir_variable **interface_vars = (nir_variable **) data;
unsigned num_fields = ifc_type->length;
glsl_struct_field *fields = malloc(sizeof(glsl_struct_field) * num_fields);
memcpy(fields, ifc_type->fields.structure,
num_fields * sizeof(*fields));
bool interface_type_changed = false;
for (unsigned i = 0; i < num_fields; i++) {
if (interface_vars[i] != NULL &&
fields[i].type != interface_vars[i]->type) {
fields[i].type = interface_vars[i]->type;
interface_type_changed = true;
}
}
if (!interface_type_changed) {
free(fields);
return;
}
enum glsl_interface_packing packing =
(enum glsl_interface_packing) ifc_type->interface_packing;
bool row_major = (bool) ifc_type->interface_row_major;
const struct glsl_type *new_ifc_type =
glsl_interface_type(fields, num_fields, packing,
row_major, glsl_get_type_name(ifc_type));
free(fields);
for (unsigned i = 0; i < num_fields; i++) {
if (interface_vars[i] != NULL)
change_interface_type(interface_vars[i], new_ifc_type);
}
}
/**
* Create a new interface type based on the given type, with unsized arrays
* replaced by sized arrays whose size is determined by
* max_ifc_array_access.
*/
static const glsl_type *
resize_interface_members(const struct glsl_type *type,
const int *max_ifc_array_access,
bool is_ssbo)
{
unsigned num_fields = type->length;
glsl_struct_field *fields = malloc(sizeof(glsl_struct_field) * num_fields); //new glsl_struct_field[num_fields];
memcpy(fields, type->fields.structure,
num_fields * sizeof(*fields));
for (unsigned i = 0; i < num_fields; i++) {
bool implicit_sized_array = fields[i].implicit_sized_array;
/* If SSBO last member is unsized array, we don't replace it by a sized
* array.
*/
if (is_ssbo && i == (num_fields - 1))
fixup_type(&fields[i].type, max_ifc_array_access[i],
true, &implicit_sized_array);
else
fixup_type(&fields[i].type, max_ifc_array_access[i],
false, &implicit_sized_array);
fields[i].implicit_sized_array = implicit_sized_array;
}
enum glsl_interface_packing packing =
(enum glsl_interface_packing) type->interface_packing;
bool row_major = (bool) type->interface_row_major;
const struct glsl_type *new_ifc_type =
glsl_interface_type(fields, num_fields,
packing, row_major, glsl_get_type_name(type));
free(fields);
return new_ifc_type;
}
/**
* Determine whether the given interface type contains unsized arrays (if
* it doesn't, array_sizing_visitor doesn't need to process it).
*/
static bool
interface_contains_unsized_arrays(const glsl_type *type)
{
for (unsigned i = 0; i < type->length; i++) {
const struct glsl_type *elem_type = type->fields.structure[i].type;
if (glsl_type_is_unsized_array(elem_type))
return true;
}
return false;
}
static const glsl_type *
update_interface_members_array(const glsl_type *type,
const glsl_type *new_interface_type)
{
const struct glsl_type *element_type = type->fields.array;
if (glsl_type_is_array(element_type)) {
const glsl_type *new_array_type =
update_interface_members_array(element_type, new_interface_type);
return glsl_array_type(new_array_type, type->length,
type->explicit_stride);
} else {
return glsl_array_type(new_interface_type, type->length,
type->explicit_stride);
}
}
static void
size_variable_array(void *mem_ctx, nir_variable *var,
struct hash_table *unnamed_interfaces)
{
const struct glsl_type *type_without_array;
const struct glsl_type *ifc_type = var->interface_type;
bool implicit_sized_array = var->data.implicit_sized_array;
fixup_type(&var->type, var->data.max_array_access,
var->data.from_ssbo_unsized_array,
&implicit_sized_array);
var->data.implicit_sized_array = implicit_sized_array;
type_without_array = glsl_without_array(var->type);
if (glsl_type_is_interface(var->type)) {
if (interface_contains_unsized_arrays(var->type)) {
const struct glsl_type *new_type =
resize_interface_members(var->type,
var->max_ifc_array_access,
var->data.mode == nir_var_mem_ssbo);
var->type = new_type;
change_interface_type(var, new_type);
}
} else if (glsl_type_is_interface(type_without_array)) {
if (interface_contains_unsized_arrays(type_without_array)) {
const struct glsl_type *new_type =
resize_interface_members(type_without_array,
var->max_ifc_array_access,
var->data.mode == nir_var_mem_ssbo);
change_interface_type(var, new_type);
var->type = update_interface_members_array(var->type, new_type);
}
} else if (ifc_type) {
/* Store a pointer to the variable in the unnamed_interfaces
* hashtable.
*/
struct hash_entry *entry =
_mesa_hash_table_search(unnamed_interfaces, ifc_type);
nir_variable **interface_vars =
entry ? (nir_variable **) entry->data : NULL;
if (interface_vars == NULL) {
interface_vars = rzalloc_array(mem_ctx, nir_variable *,
ifc_type->length);
_mesa_hash_table_insert(unnamed_interfaces, ifc_type,
interface_vars);
}
unsigned index = glsl_get_field_index(ifc_type, var->name);
assert(index < ifc_type->length);
assert(interface_vars[index] == NULL);
interface_vars[index] = var;
}
}
void
gl_nir_linker_size_arrays(nir_shader *shader)
{
void *mem_ctx = ralloc_context(NULL);
/**
* Hash table from const glsl_type * to an array of nir_variable *'s
* pointing to the nir_variables constituting each unnamed interface block.
*/
struct hash_table *unnamed_interfaces =
_mesa_pointer_hash_table_create(NULL);
nir_foreach_variable_in_shader(var, shader) {
size_variable_array(mem_ctx, var, unnamed_interfaces);
}
nir_foreach_function_impl(impl, shader) {
nir_foreach_variable_in_list(var, &impl->locals) {
size_variable_array(mem_ctx, var, unnamed_interfaces);
}
}
/**
* For each unnamed interface block that was discovered while running the
* visitor, adjust the interface type to reflect the newly assigned array
* sizes, and fix up the nir_variable nodes to point to the new interface
* type.
*/
hash_table_call_foreach(unnamed_interfaces,
fixup_unnamed_interface_type, NULL);
_mesa_hash_table_destroy(unnamed_interfaces, NULL);
ralloc_free(mem_ctx);
}
/**
* Return true if interface members mismatch and its not allowed by GLSL.

View file

@ -91,6 +91,8 @@ bool gl_nir_validate_intrastage_arrays(struct gl_shader_program *prog,
nir_shader *existing_shader,
bool match_precision);
void gl_nir_linker_size_arrays(nir_shader *shader);
struct nir_xfb_info *
gl_to_nir_xfb_info(struct gl_transform_feedback_info *info, void *mem_ctx);