diff --git a/3rdparty/spirv-cross/spirv_cross_parsed_ir.cpp b/3rdparty/spirv-cross/spirv_cross_parsed_ir.cpp index ce2f98f13..d399d4159 100644 --- a/3rdparty/spirv-cross/spirv_cross_parsed_ir.cpp +++ b/3rdparty/spirv-cross/spirv_cross_parsed_ir.cpp @@ -349,7 +349,7 @@ void ParsedIR::set_name(ID id, const string &name) void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name) { auto &m = meta[id]; - m.members.resize(max(meta[id].members.size(), size_t(index) + 1)); + m.members.resize(max(m.members.size(), size_t(index) + 1)); m.members[index].alias = name; if (!is_valid_identifier(name) || is_reserved_identifier(name, true, false)) meta_needing_name_fixup.insert(id); @@ -451,8 +451,9 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument) void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument) { - meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1)); - auto &dec = meta[id].members[index]; + auto &m = meta[id]; + m.members.resize(max(m.members.size(), size_t(index) + 1)); + auto &dec = m.members[index]; dec.decoration_flags.set(decoration); switch (decoration) @@ -792,7 +793,8 @@ const Bitset &ParsedIR::get_decoration_bitset(ID id) const void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument) { - meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1)); + auto &m = meta[id]; + m.members.resize(max(m.members.size(), size_t(index) + 1)); auto &dec = meta[id].members[index]; dec.decoration_flags.set(decoration); diff --git a/3rdparty/spirv-cross/spirv_glsl.cpp b/3rdparty/spirv-cross/spirv_glsl.cpp index 52cef4ec2..f25c3b42c 100644 --- a/3rdparty/spirv-cross/spirv_glsl.cpp +++ b/3rdparty/spirv-cross/spirv_glsl.cpp @@ -656,6 +656,7 @@ string CompilerGLSL::compile() backend.support_case_fallthrough = false; // Scan the SPIR-V to find trivial uses of extensions. + fixup_anonymous_struct_names(); fixup_type_alias(); reorder_type_alias(); build_function_control_flow_graphs_and_analyze(); @@ -15791,6 +15792,52 @@ void CompilerGLSL::reset_name_caches() function_overloads.clear(); } +void CompilerGLSL::fixup_anonymous_struct_names(std::unordered_set &visited, const SPIRType &type) +{ + if (visited.count(type.self)) + return; + visited.insert(type.self); + + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + auto &mbr_type = get(type.member_types[i]); + + if (mbr_type.basetype == SPIRType::Struct) + { + // If there are multiple aliases, the output might be somewhat unpredictable, + // but the only real alternative in that case is to do nothing, which isn't any better. + // This check should be fine in practice. + if (get_name(mbr_type.self).empty() && !get_member_name(type.self, i).empty()) + { + auto anon_name = join("anon_", get_member_name(type.self, i)); + ParsedIR::sanitize_underscores(anon_name); + set_name(mbr_type.self, anon_name); + } + + fixup_anonymous_struct_names(visited, mbr_type); + } + } +} + +void CompilerGLSL::fixup_anonymous_struct_names() +{ + // HLSL codegen can often end up emitting anonymous structs inside blocks, which + // breaks GL linking since all names must match ... + // Try to emit sensible code, so attempt to find such structs and emit anon_$member. + + // Breaks exponential explosion with weird type trees. + std::unordered_set visited; + + ir.for_each_typed_id([&](uint32_t, SPIRType &type) { + if (type.basetype == SPIRType::Struct && + (has_decoration(type.self, DecorationBlock) || + has_decoration(type.self, DecorationBufferBlock))) + { + fixup_anonymous_struct_names(visited, type); + } + }); +} + void CompilerGLSL::fixup_type_alias() { // Due to how some backends work, the "master" type of type_alias must be a block-like type if it exists. diff --git a/3rdparty/spirv-cross/spirv_glsl.hpp b/3rdparty/spirv-cross/spirv_glsl.hpp index b892e0c3b..7297239ff 100644 --- a/3rdparty/spirv-cross/spirv_glsl.hpp +++ b/3rdparty/spirv-cross/spirv_glsl.hpp @@ -946,6 +946,8 @@ protected: void fixup_type_alias(); void reorder_type_alias(); + void fixup_anonymous_struct_names(); + void fixup_anonymous_struct_names(std::unordered_set &visited, const SPIRType &type); static const char *vector_swizzle(int vecsize, int index); diff --git a/3rdparty/spirv-cross/spirv_hlsl.cpp b/3rdparty/spirv-cross/spirv_hlsl.cpp index d0d0e15ac..e44ed846c 100644 --- a/3rdparty/spirv-cross/spirv_hlsl.cpp +++ b/3rdparty/spirv-cross/spirv_hlsl.cpp @@ -5820,6 +5820,7 @@ string CompilerHLSL::compile() // SM 4.1 does not support precise for some reason. backend.support_precise_qualifier = hlsl_options.shader_model >= 50 || hlsl_options.shader_model == 40; + fixup_anonymous_struct_names(); fixup_type_alias(); reorder_type_alias(); build_function_control_flow_graphs_and_analyze(); diff --git a/3rdparty/spirv-cross/spirv_msl.cpp b/3rdparty/spirv-cross/spirv_msl.cpp index 8ea27d5f6..75551b46e 100644 --- a/3rdparty/spirv-cross/spirv_msl.cpp +++ b/3rdparty/spirv-cross/spirv_msl.cpp @@ -1371,6 +1371,7 @@ string CompilerMSL::compile() for (auto &id : next_metal_resource_ids) id = 0; + fixup_anonymous_struct_names(); fixup_type_alias(); replace_illegal_names(); sync_entry_point_aliases_and_names(); @@ -3132,6 +3133,14 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st return; } + // If variable names alias, they will end up with wrong names in the interface struct, because + // there might be aliases in the member name cache and there would be a mismatch in fixup_in code. + // Make sure to register the variables as unique resource names ahead of time. + // This would normally conflict with the name cache when emitting local variables, + // but this happens in the setup stage, before we hit compilation loops. + // The name cache is cleared before we actually emit code, so this is safe. + add_resource_name(var.self); + if (var_type.basetype == SPIRType::Struct) { bool block_requires_flattening = variable_storage_requires_stage_io(storage) || is_block; @@ -3189,7 +3198,7 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st if (storage == StorageClassOutput && is_stage_output_block_member_masked(var, mbr_idx, meta.strip_array)) { - location = UINT32_MAX; // Skip this member and resolve location again on next var member + location = UINT32_MAX; // Skip this member and resolve location again on next var member if (is_block) masked_block = true; @@ -3226,16 +3235,18 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st { bool is_composite_type = is_matrix(mbr_type) || is_array(mbr_type) || mbr_type.basetype == SPIRType::Struct; bool attribute_load_store = - storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; bool storage_is_stage_io = variable_storage_requires_stage_io(storage); // Clip/CullDistance always need to be declared as user attributes. if (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance) is_builtin = false; - string mbr_name_qual = to_name(var_type.self); - string var_chain_qual = to_name(var.self); - if (elem_cnt > 1) { + const string var_name = to_name(var.self); + string mbr_name_qual = var_name; + string var_chain_qual = var_name; + if (elem_cnt > 1) + { mbr_name_qual += join("_", elem_idx); var_chain_qual += join("[", elem_idx, "]"); }