From 083b4ffb7897f6d4c155829256078ca0f668f532 Mon Sep 17 00:00:00 2001 From: Timothy Arceri Date: Thu, 30 May 2024 13:11:02 +1000 Subject: [PATCH] glsl: add gl_nir_linker_size_arrays() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will size implicitly size arrays during linking, replacing the glsl ir version. Acked-by: Marek Olšák Part-of: --- .../glsl/gl_nir_link_interface_blocks.c | 225 ++++++++++++++++++ src/compiler/glsl/gl_nir_linker.h | 2 + 2 files changed, 227 insertions(+) diff --git a/src/compiler/glsl/gl_nir_link_interface_blocks.c b/src/compiler/glsl/gl_nir_link_interface_blocks.c index 4c0a7c748ed..999a48872e6 100644 --- a/src/compiler/glsl/gl_nir_link_interface_blocks.c +++ b/src/compiler/glsl/gl_nir_link_interface_blocks.c @@ -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. diff --git a/src/compiler/glsl/gl_nir_linker.h b/src/compiler/glsl/gl_nir_linker.h index 549dbe3c527..800b3c44c4a 100644 --- a/src/compiler/glsl/gl_nir_linker.h +++ b/src/compiler/glsl/gl_nir_linker.h @@ -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);