diff --git a/reference/opt/shaders/legacy/fragment/switch.legacy.frag b/reference/opt/shaders/legacy/fragment/switch.legacy.frag new file mode 100644 index 00000000..21a4d673 --- /dev/null +++ b/reference/opt/shaders/legacy/fragment/switch.legacy.frag @@ -0,0 +1,77 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp float vIndexF; + +void main() +{ + int _13 = int(vIndexF); + highp vec4 _65; + highp vec4 _66; + highp vec4 _68; + for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++) + { + if (_13 == 2) + { + _68 = vec4(0.0, 2.0, 3.0, 4.0); + break; + } + else if ((_13 == 4) || (_13 == 5)) + { + _68 = vec4(1.0, 2.0, 3.0, 4.0); + break; + } + else if ((_13 == 8) || (_13 == 9)) + { + _68 = vec4(40.0, 20.0, 30.0, 40.0); + break; + } + else if (_13 == 10) + { + _65 = vec4(10.0); + highp vec4 _45 = _65 + vec4(1.0); + _66 = _45; + highp vec4 _48 = _66 + vec4(2.0); + _68 = _48; + break; + } + else if (_13 == 11) + { + _65 = vec4(0.0); + highp vec4 _45 = _65 + vec4(1.0); + _66 = _45; + highp vec4 _48 = _66 + vec4(2.0); + _68 = _48; + break; + } + else if (_13 == 12) + { + _66 = vec4(0.0); + highp vec4 _48 = _66 + vec4(2.0); + _68 = _48; + break; + } + else + { + _68 = vec4(10.0, 20.0, 30.0, 40.0); + break; + } + } + highp vec4 _70; + for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++) + { + if ((_13 == 10) || (_13 == 20)) + { + _70 = vec4(40.0); + break; + } + else + { + _70 = vec4(20.0); + break; + } + } + gl_FragData[0] = _68 + _70; +} + diff --git a/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag index ba66ccf6..19af59d3 100644 --- a/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag +++ b/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -17,14 +17,6 @@ void frag_main() int j; int _30; int _31; - if (vIndex != 0 && vIndex != 1 && vIndex != 11 && vIndex != 2 && vIndex != 3 && vIndex != 4 && vIndex != 5) - { - _30 = 2; - } - if (vIndex == 1 || vIndex == 11) - { - _31 = 1; - } switch (vIndex) { case 0: @@ -37,6 +29,7 @@ void frag_main() } default: { + _30 = 2; j = _30; _31 = 0; j = _31; @@ -45,6 +38,7 @@ void frag_main() case 1: case 11: { + _31 = 1; j = _31; break; } diff --git a/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag b/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag new file mode 100644 index 00000000..d36e7827 --- /dev/null +++ b/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag @@ -0,0 +1,24 @@ +#version 100 +precision mediump float; +precision highp int; + +vec2 _19; + +void main() +{ + highp vec2 _30; + for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++) + { + if (gl_FragCoord.x != gl_FragCoord.x) + { + _30 = _19; + break; + } + highp vec2 _29 = _19; + _29.y = _19.y; + _30 = _29; + break; + } + gl_FragData[0] = vec4(_30, 1.0, 1.0); +} + diff --git a/reference/shaders/legacy/fragment/switch.legacy.frag b/reference/shaders/legacy/fragment/switch.legacy.frag new file mode 100644 index 00000000..9960ce7e --- /dev/null +++ b/reference/shaders/legacy/fragment/switch.legacy.frag @@ -0,0 +1,78 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp float vIndexF; + +void main() +{ + int vIndex = int(vIndexF); + highp vec4 v = vec4(0.0); + for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++) + { + if (vIndex == 2) + { + v = vec4(0.0, 2.0, 3.0, 4.0); + break; + } + else if ((vIndex == 4) || (vIndex == 5)) + { + v = vec4(1.0, 2.0, 3.0, 4.0); + break; + } + else if ((vIndex == 8) || (vIndex == 9)) + { + v = vec4(40.0, 20.0, 30.0, 40.0); + break; + } + else if (vIndex == 10) + { + v = vec4(10.0); + highp vec4 _43 = v; + highp vec4 _44 = vec4(1.0); + highp vec4 _45 = _43 + _44; + v = _45; + highp vec4 _46 = v; + highp vec4 _47 = vec4(2.0); + highp vec4 _48 = _46 + _47; + v = _48; + break; + } + else if (vIndex == 11) + { + highp vec4 _43 = v; + highp vec4 _44 = vec4(1.0); + highp vec4 _45 = _43 + _44; + v = _45; + highp vec4 _46 = v; + highp vec4 _47 = vec4(2.0); + highp vec4 _48 = _46 + _47; + v = _48; + break; + } + else if (vIndex == 12) + { + highp vec4 _46 = v; + highp vec4 _47 = vec4(2.0); + highp vec4 _48 = _46 + _47; + v = _48; + break; + } + else + { + v = vec4(10.0, 20.0, 30.0, 40.0); + break; + } + } + highp vec4 w = vec4(20.0); + for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++) + { + if ((vIndex == 10) || (vIndex == 20)) + { + w = vec4(40.0); + break; + } + } + gl_FragData[0] = v + w; +} + diff --git a/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag b/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag new file mode 100644 index 00000000..d2bd15a9 --- /dev/null +++ b/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 54 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %_GLF_color "_GLF_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_GLF_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %bool = OpTypeBool + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %_GLF_color = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %52 = OpUndef %v2float + %main = OpFunction %void None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpSwitch %int_0 %8 + %8 = OpLabel + %17 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %18 = OpLoad %float %17 + %22 = OpFOrdNotEqual %bool %18 %18 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpBranch %9 + %24 = OpLabel + %33 = OpCompositeExtract %float %52 1 + %51 = OpCompositeInsert %v2float %33 %52 1 + OpBranch %9 + %9 = OpLabel + %53 = OpPhi %v2float %52 %23 %51 %24 + %42 = OpCompositeExtract %float %53 0 + %43 = OpCompositeExtract %float %53 1 + %48 = OpCompositeConstruct %v4float %42 %43 %float_1 %float_1 + OpStore %_GLF_color %48 + OpReturn + OpFunctionEnd diff --git a/shaders/legacy/fragment/switch.legacy.frag b/shaders/legacy/fragment/switch.legacy.frag new file mode 100644 index 00000000..d5117981 --- /dev/null +++ b/shaders/legacy/fragment/switch.legacy.frag @@ -0,0 +1,43 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in float vIndexF; + +void main() +{ + int vIndex = int(vIndexF); + vec4 v = vec4(0.0); + switch (vIndex) + { + case 2: + v = vec4(0, 2, 3, 4); + break; + case 4: + case 5: + v = vec4(1, 2, 3, 4); + break; + case 8: + case 9: + v = vec4(40, 20, 30, 40); + break; + case 10: + v = vec4(10.0); + case 11: + v += 1.0; + case 12: + v += 2.0; + break; + default: + v = vec4(10, 20, 30, 40); + break; + } + + vec4 w = vec4(20.0); + switch (vIndex) + { + case 10: + case 20: + w = vec4(40.0); + } + FragColor = v + w; +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 8d8241e6..5f2a48f0 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -540,6 +540,9 @@ string CompilerGLSL::compile() backend.supports_extensions = true; backend.use_array_constructor = true; + if (is_legacy_es()) + backend.support_case_fallthrough = false; + // Scan the SPIR-V to find trivial uses of extensions. fixup_type_alias(); reorder_type_alias(); @@ -13295,6 +13298,8 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) // Order does not matter. if (!injected_block) block_declaration_order.push_back(block.default_block); + else if (is_legacy_es()) + SPIRV_CROSS_THROW("Default case label fallthrough to other case label is not supported in ESSL 1.0."); case_constructs[block.default_block] = {}; } @@ -13305,12 +13310,25 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) return is_unsigned_case ? convert_to_string(literal) : convert_to_string(int32_t(literal)); }; + const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector &labels, const char *suffix) -> string { + string ret; + size_t count = labels.size(); + for (size_t i = 0; i < count; i++) + { + if (i) + ret += " || "; + ret += join(count > 1 ? "(" : "", to_enclosed_expression(condition), " == ", labels[i], suffix, + count > 1 ? ")" : ""); + } + return ret; + }; + // We need to deal with a complex scenario for OpPhi. If we have case-fallthrough and Phi in the picture, // we need to flush phi nodes outside the switch block in a branch, // and skip any Phi handling inside the case label to make fall-through work as expected. // This kind of code-gen is super awkward and it's a last resort. Normally we would want to handle this // inside the case label if at all possible. - for (size_t i = 1; i < num_blocks; i++) + for (size_t i = 1; backend.support_case_fallthrough && i < num_blocks; i++) { if (flush_phi_required(block.self, block_declaration_order[i]) && flush_phi_required(block_declaration_order[i - 1], block_declaration_order[i])) @@ -13364,8 +13382,14 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &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 && block.cases.empty(); - if (degenerate_switch) - statement("do"); + if (degenerate_switch || is_legacy_es()) + { + // ESSL 1.0 is not guaranteed to support do/while. + if (is_legacy_es()) + statement("for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)"); + else + statement("do"); + } else { emit_block_hints(block); @@ -13382,14 +13406,28 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) { // Default case. if (!degenerate_switch) - statement("default:"); + { + if (is_legacy_es()) + statement("else"); + else + statement("default:"); + } } else { - for (auto &case_literal : literals) + if (is_legacy_es()) { - // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here. - statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); + statement((i ? "else " : ""), "if (", + to_legacy_case_label(block.condition, literals, label_suffix), + ")"); + } + else + { + for (auto &case_literal : literals) + { + // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here. + statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); + } } } @@ -13424,7 +13462,12 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); if (block.default_block == block.next_block) - statement("default:"); + { + if (is_legacy_es()) + statement("else"); + else + statement("default:"); + } begin_scope(); flush_phi(block.self, block.next_block); @@ -13433,7 +13476,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) } } - if (degenerate_switch) + if (degenerate_switch && !is_legacy_es()) end_scope_decl("while(false)"); else end_scope(); diff --git a/test_shaders.py b/test_shaders.py index c0130bf4..b4558734 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -492,7 +492,7 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl remove_file(glsl_path) glsl_path = None - if vulkan or spirv: + if (vulkan or spirv) and (not is_legacy): subprocess.check_call([spirv_cross_path, '--entry', 'main', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path] + extra_args) validate_shader(vulkan_glsl_path, True, paths) # SPIR-V shaders might just want to validate Vulkan GLSL output, we don't always care about the output.