diff options
author | Hans-Kristian Arntzen <post@arntzen-software.no> | 2022-07-04 16:17:52 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-04 16:17:52 +0300 |
commit | d8d051381f65b9606fb8016c79b7c3bab872eec3 (patch) | |
tree | b915cff2bfc89990e7b64301bd1a8f60eedf0c67 | |
parent | f46745095d70ebda0887e62a7ee8545d11dd6bbc (diff) | |
parent | 963fdfdf68647680c6c2bf119ad98ea05d3d8235 (diff) |
Merge pull request #1970 from KhronosGroup/fix-1969
Handle PHI in collapsed switch constructs.
-rw-r--r-- | reference/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag | 11 | ||||
-rw-r--r-- | shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag | 38 | ||||
-rw-r--r-- | spirv_glsl.cpp | 53 |
3 files changed, 81 insertions, 21 deletions
diff --git a/reference/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag b/reference/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag new file mode 100644 index 00000000..a6f3e694 --- /dev/null +++ b/reference/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 _17; + _17 = vec4(1.0); + FragColor = _17; +} + diff --git a/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag b/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag new file mode 100644 index 00000000..d5a07b54 --- /dev/null +++ b/shaders-no-opt/asm/frag/collapsed-switch-phi-flush.asm.frag @@ -0,0 +1,38 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vIndex + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vIndex "vIndex" + OpDecorate %FragColor Location 0 + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %15 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %main = OpFunction %void None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpSwitch %int_0 %9 + %9 = OpLabel + %tmp = OpPhi %v4float %15 %5 + OpStore %FragColor %tmp + OpReturn + OpFunctionEnd diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 6af72df6..9936c404 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -15753,26 +15753,32 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) // If there is only one default block, and no cases, this is a case where SPIRV-opt decided to emulate // non-structured exits with the help of a switch block. // This is buggy on FXC, so just emit the logical equivalent of a do { } while(false), which is more idiomatic. - bool degenerate_switch = block.default_block != block.merge_block && cases.empty(); + bool block_like_switch = cases.empty(); - if (degenerate_switch || is_legacy_es()) + // If this is true, the switch is completely meaningless, and we should just avoid it. + bool collapsed_switch = block_like_switch && block.default_block == block.next_block; + + if (!collapsed_switch) { - // ESSL 1.0 is not guaranteed to support do/while. - if (is_legacy_es()) + if (block_like_switch || is_legacy_es()) { - uint32_t counter = statement_count; - statement("for (int spvDummy", counter, " = 0; spvDummy", counter, - " < 1; spvDummy", counter, "++)"); + // ESSL 1.0 is not guaranteed to support do/while. + if (is_legacy_es()) + { + uint32_t counter = statement_count; + statement("for (int spvDummy", counter, " = 0; spvDummy", counter, " < 1; spvDummy", counter, + "++)"); + } + else + statement("do"); } else - statement("do"); - } - else - { - emit_block_hints(block); - statement("switch (", to_unpacked_expression(block.condition), ")"); + { + emit_block_hints(block); + statement("switch (", to_unpacked_expression(block.condition), ")"); + } + begin_scope(); } - begin_scope(); for (size_t i = 0; i < num_blocks; i++) { @@ -15782,7 +15788,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) if (literals.empty()) { // Default case. - if (!degenerate_switch) + if (!block_like_switch) { if (is_legacy_es()) statement("else"); @@ -15820,10 +15826,10 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) else current_emitting_switch_fallthrough = false; - if (!degenerate_switch) + if (!block_like_switch) begin_scope(); branch(block.self, target_block); - if (!degenerate_switch) + if (!block_like_switch) end_scope(); current_emitting_switch_fallthrough = false; @@ -15837,7 +15843,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) // - Header -> Merge requires flushing PHI. In this case, we need to collect all cases and flush PHI there. bool header_merge_requires_phi = flush_phi_required(block.self, block.next_block); bool need_fallthrough_block = block.default_block == block.next_block || !literals_to_merge.empty(); - if ((header_merge_requires_phi && need_fallthrough_block) || !literals_to_merge.empty()) + if (!collapsed_switch && ((header_merge_requires_phi && need_fallthrough_block) || !literals_to_merge.empty())) { for (auto &case_literal : literals_to_merge) statement("case ", to_case_label(case_literal, type.width, unsigned_case), label_suffix, ":"); @@ -15856,10 +15862,15 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) end_scope(); } - if (degenerate_switch && !is_legacy_es()) - end_scope_decl("while(false)"); + if (!collapsed_switch) + { + if (block_like_switch && !is_legacy_es()) + end_scope_decl("while(false)"); + else + end_scope(); + } else - end_scope(); + flush_phi(block.self, block.next_block); if (block.need_ladder_break) { |