diff --git a/reference/opt/shaders/asm/comp/name-alias.asm.invalid.comp b/reference/opt/shaders/asm/comp/name-alias.asm.invalid.comp index b2759100..870b1df9 100644 --- a/reference/opt/shaders/asm/comp/name-alias.asm.invalid.comp +++ b/reference/opt/shaders/asm/comp/name-alias.asm.invalid.comp @@ -19,19 +19,19 @@ struct alias_2 alias_1 alias_1; }; -layout(binding = 0, std430) buffer _10_11 -{ - alias_2 alias; -} alias_3; - -layout(binding = 1, std140) buffer _15_16 +layout(binding = 0, std430) buffer alias_3 { alias_2 alias; } alias_4; +layout(binding = 1, std140) buffer alias_5 +{ + alias_2 alias; +} alias_6; + void main() { - alias_2 alias_5 = alias_3.alias; - alias_4.alias = alias_5; + alias_2 alias_7 = alias_4.alias; + alias_6.alias = alias_7; } diff --git a/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag b/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag new file mode 100644 index 00000000..2d0809fd --- /dev/null +++ b/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(binding = 0, std430) buffer Foobar +{ + vec4 _data[]; +} Foobar_1; + +layout(binding = 1, std430) buffer Foobaz +{ + vec4 _data[]; +} Foobaz_1; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + _entryPointOutput = Foobar_1._data[0] + Foobaz_1._data[0]; +} + diff --git a/reference/shaders/asm/comp/name-alias.asm.invalid.comp b/reference/shaders/asm/comp/name-alias.asm.invalid.comp index b2759100..870b1df9 100644 --- a/reference/shaders/asm/comp/name-alias.asm.invalid.comp +++ b/reference/shaders/asm/comp/name-alias.asm.invalid.comp @@ -19,19 +19,19 @@ struct alias_2 alias_1 alias_1; }; -layout(binding = 0, std430) buffer _10_11 -{ - alias_2 alias; -} alias_3; - -layout(binding = 1, std140) buffer _15_16 +layout(binding = 0, std430) buffer alias_3 { alias_2 alias; } alias_4; +layout(binding = 1, std140) buffer alias_5 +{ + alias_2 alias; +} alias_6; + void main() { - alias_2 alias_5 = alias_3.alias; - alias_4.alias = alias_5; + alias_2 alias_7 = alias_4.alias; + alias_6.alias = alias_7; } diff --git a/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag b/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag new file mode 100644 index 00000000..70843a85 --- /dev/null +++ b/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(binding = 0, std430) buffer Foobar +{ + vec4 _data[]; +} Foobar_1; + +layout(binding = 1, std430) buffer Foobaz +{ + vec4 _data[]; +} Foobaz_1; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main() +{ + return Foobar_1._data[0] + Foobaz_1._data[0]; +} + +void main() +{ + _entryPointOutput = _main(); +} + diff --git a/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag b/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag new file mode 100644 index 00000000..1c6dd7b8 --- /dev/null +++ b/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %Foobar "Foobar" + OpMemberName %Foobar 0 "@data" + OpName %Foobar_0 "Foobar" + OpName %Foobaz "Foobaz" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %Foobar 0 Offset 0 + OpDecorate %Foobar BufferBlock + OpDecorate %Foobar_0 DescriptorSet 0 + OpDecorate %Foobar_0 Binding 0 + OpDecorate %Foobaz DescriptorSet 0 + OpDecorate %Foobaz Binding 1 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %Foobar = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_Foobar = OpTypePointer Uniform %Foobar + %Foobar_0 = OpVariable %_ptr_Uniform_Foobar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %Foobaz = OpVariable %_ptr_Uniform_Foobar Uniform +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %28 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %28 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %18 = OpAccessChain %_ptr_Uniform_v4float %Foobar_0 %int_0 %int_0 + %19 = OpLoad %v4float %18 + %21 = OpAccessChain %_ptr_Uniform_v4float %Foobaz %int_0 %int_0 + %22 = OpLoad %v4float %21 + %23 = OpFAdd %v4float %19 %22 + OpReturnValue %23 + OpFunctionEnd diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 5c9cb916..a9623f35 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -679,7 +679,7 @@ ShaderResources Compiler::get_shader_resources(const unordered_set *ac if (var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self)) { if (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) - res.stage_inputs.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias }); + res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) }); else res.stage_inputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } @@ -692,7 +692,7 @@ ShaderResources Compiler::get_shader_resources(const unordered_set *ac else if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self)) { if (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) - res.stage_outputs.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias }); + res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) }); else res.stage_outputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias }); } @@ -700,24 +700,21 @@ ShaderResources Compiler::get_shader_resources(const unordered_set *ac else if (type.storage == StorageClassUniform && (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock))) { - auto &block_name = meta[type.self].decoration.alias; res.uniform_buffers.push_back({ var.self, var.basetype, type.self, - block_name.empty() ? get_block_fallback_name(var.self) : block_name }); + get_remapped_declared_block_name(var.self) }); } // Old way to declare SSBOs. else if (type.storage == StorageClassUniform && (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock))) { - auto &block_name = meta[type.self].decoration.alias; res.storage_buffers.push_back({ var.self, var.basetype, type.self, - block_name.empty() ? get_block_fallback_name(var.self) : block_name }); + get_remapped_declared_block_name(var.self) }); } // Modern way to declare SSBOs. else if (type.storage == StorageClassStorageBuffer) { - auto &block_name = meta[type.self].decoration.alias; res.storage_buffers.push_back({ var.self, var.basetype, type.self, - block_name.empty() ? get_block_fallback_name(var.self) : block_name }); + get_remapped_declared_block_name(var.self) }); } // Push constant blocks else if (type.storage == StorageClassPushConstant) @@ -1180,7 +1177,10 @@ const std::string Compiler::get_fallback_name(uint32_t id) const const std::string Compiler::get_block_fallback_name(uint32_t id) const { auto &var = get(id); - return join("_", get(var.basetype).self, "_", id); + if (get_name(id).empty()) + return join("_", get(var.basetype).self, "_", id); + else + return get_name(id); } uint64_t Compiler::get_decoration_mask(uint32_t id) const @@ -3802,3 +3802,17 @@ const std::vector &Compiler::get_declared_extensions() const { return declared_extensions; } + +std::string Compiler::get_remapped_declared_block_name(uint32_t id) const +{ + auto itr = declared_block_names.find(id); + if (itr != end(declared_block_names)) + return itr->second; + else + { + auto &var = get(id); + auto &type = get(var.basetype); + auto &block_name = meta[type.self].decoration.alias; + return block_name.empty() ? get_block_fallback_name(id) : block_name; + } +} diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 52dafcb3..cf285e31 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -410,6 +410,19 @@ public: // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. const std::vector &get_declared_extensions() const; + // When declaring buffer blocks in GLSL, the name declared in the GLSL source + // might not be the same as the name declared in the SPIR-V module due to naming conflicts. + // In this case, SPIRV-Cross needs to find a fallback-name, and it might only + // be possible to know this name after compiling to GLSL. + // This is particularly important for HLSL input and UAVs which tends to reuse the same block type + // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself + // because it might be unique. Instead, you can use this interface to check after compilation which + // name was actually used if your input SPIR-V tends to have this problem. + // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables + // after compiling, block names are an exception to this rule. + // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. + std::string get_remapped_declared_block_name(uint32_t id) const; + protected: const uint32_t *stream(const Instruction &instr) const { @@ -729,6 +742,7 @@ protected: std::vector declared_capabilities; std::vector declared_extensions; + std::unordered_map declared_block_names; }; } diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 3feae239..271ad7a0 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -1374,17 +1374,24 @@ void CompilerGLSL::emit_buffer_block_native(const SPIRVariable &var) bool is_readonly = ssbo && (flags & (1ull << DecorationNonWritable)) != 0; bool is_coherent = ssbo && (flags & (1ull << DecorationCoherent)) != 0; - add_resource_name(var.self); - - // Block names should never alias. + // Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ... auto buffer_name = to_name(type.self, false); // Shaders never use the block by interface name, so we don't // have to track this other than updating name caches. if (meta[type.self].decoration.alias.empty() || resource_names.find(buffer_name) != end(resource_names)) buffer_name = get_block_fallback_name(var.self); - else - resource_names.insert(buffer_name); + + // Make sure we get something unique. + add_variable(resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + if (buffer_name.empty()) + buffer_name = join("_", get(var.basetype).self, "_", var.self); + + // Save for post-reflection later. + declared_block_names[var.self] = buffer_name; statement(layout_for_variable(var), is_coherent ? "coherent " : "", is_restrict ? "restrict " : "", is_writeonly ? "writeonly " : "", is_readonly ? "readonly " : "", ssbo ? "buffer " : "uniform ", @@ -1402,6 +1409,7 @@ void CompilerGLSL::emit_buffer_block_native(const SPIRVariable &var) i++; } + add_resource_name(var.self); end_scope_decl(to_name(var.self) + type_to_array_glsl(type)); statement(""); } @@ -1521,8 +1529,6 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var) require_extension("GL_EXT_shader_io_blocks"); } - add_resource_name(var.self); - // Block names should never alias. auto block_name = to_name(type.self, false); @@ -1546,6 +1552,7 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var) i++; } + add_resource_name(var.self); end_scope_decl(join(to_name(var.self), type_to_array_glsl(type))); statement(""); } @@ -7249,9 +7256,8 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) } } -void CompilerGLSL::add_variable(unordered_set &variables, uint32_t id) +void CompilerGLSL::add_variable(unordered_set &variables, string &name) { - auto &name = meta[id].decoration.alias; if (name.empty()) return; @@ -7265,6 +7271,12 @@ void CompilerGLSL::add_variable(unordered_set &variables, uint32_t id) update_name_cache(variables, name); } +void CompilerGLSL::add_variable(unordered_set &variables, uint32_t id) +{ + auto &name = meta[id].decoration.alias; + add_variable(variables, name); +} + void CompilerGLSL::add_local_variable_name(uint32_t id) { add_variable(local_variable_names, id); diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 1e316ca9..8bbfdce2 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -488,6 +488,7 @@ protected: void remap_pls_variables(); void add_variable(std::unordered_set &variables, uint32_t id); + void add_variable(std::unordered_set &variables, std::string &name); void check_function_call_constraints(const uint32_t *args, uint32_t length); void handle_invalid_expression(uint32_t id); void find_static_extensions();