From 2da12dca5803c3dcde920a1cff838fd98aecb36f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 4 Jul 2018 22:50:02 +0200 Subject: [PATCH] Squashed 'deps/SPIRV-Cross/' changes from e59cc24495..f96c9f9fb4 f96c9f9fb4 Merge pull request #635 from KhronosGroup/fix-631 e044732896 Support OpTypeImage with depth == 2 (unknown) properly. a6814a405a Merge pull request #630 from KhronosGroup/fix-628 af2d3abd03 Fail more gracefully with some unsupported opcodes. 26107ba200 Fix os.errno issues on Travis. ee44f6027b Changed OpTypeImage to only flag depth if the op is 1 c863f53cac Merge pull request #627 from KhronosGroup/macro-namespace-fix 9ddbd5aff6 Run format_all.sh. f1752e58e1 Add basic namespace to internal macros. d67e586b2e Merge pull request #626 from billhollings/master 9bf226cb05 Fixes for code review of PR 626. 4c5142b9d3 CompilerMSL support larger texel buffers by using 2D Metal textures. 314f39a7c4 Merge pull request #621 from billhollings/master 4beefe756c Fixes from PR 621 code review. f66507a701 Merge branch 'master' of https://github.com/KhronosGroup/SPIRV-Cross 0ea5e0549e Merge pull request #615 from JustSid/master 5ac55ee735 Fixed emission of some legacy texture ops without requiring the appropriate extensions ceec708b89 Added better fallbacks for legacy textureProjLod() and textureProjLodOffset() generation 994f789465 Merge pull request #624 from KhronosGroup/fix-619 33c61d2abe Support branch/loop hints in HLSL. 327fb03677 Merge pull request #623 from KhronosGroup/fix-618 2077478651 Merge pull request #622 from KhronosGroup/fix-620 10dfaf79d5 Support globallycoherent in HLSL. ffa9133d77 Support ternary expressions in OpSpecConstantOp. e091031613 CompilerMSL pass builtin struct members into functions. 7607eb6923 Merge pull request #617 from KhronosGroup/fix-612 d94d20f4f3 Deal with some builtins being declared with wrong signedness. 0f62b5dc1e Moved check for depth texture and shadowXY emission completely to legacy_tex_op() 447a253ce7 Simplified check for depth texture 76c8e3c1c4 Merge pull request #616 from KhronosGroup/fix-614 b29629fd46 Add support to remove SPIRV_Cross_BaseInstance uniform. 809631ce21 Mention JSON backend in README. 040204d65c Fix warnings and run format_all.sh. b4c8c3b9b2 Merge branch 'reflection' of git://github.com/jherico/SPIRV-Cross f6dad78c99 Added support for shadowXY() sample instructions in legacy GLSL 9ad432463c Prefix integer types with underscore 0ad0f948e1 More PR feedback 3b30202bee Add reflection specific test cases, add reflection testing to test_shaders.sh 8d84a541ac Add specialization constant output in reflection 762040084d More feedback d0a67ba6a7 Code consolidation, const correctness, faster regression testing 3a825349bc More cleanup 6c88b0048b PR feedback ee86000529 Cleanup code 709d3c60f2 Working on reflection output 0039cb86fc Merge pull request #613 from KhronosGroup/fix-609 9d31154917 Deal with switch case labels which share a block. git-subtree-dir: deps/SPIRV-Cross git-subtree-split: f96c9f9fb4fc7d17991cecb2b2294dce06d08d9c --- CMakeLists.txt | 6 +- README.md | 1 + main.cpp | 41 +- msvc/SPIRV-Cross.vcxproj | 2 + msvc/SPIRV-Cross.vcxproj.filters | 6 + .../asm/comp/control-flow-hints.asm.comp | 32 + .../asm/frag/unknown-depth-state.asm.frag | 31 + .../vert/uint-vertex-id-instance-id.asm.vert | 28 + .../shaders-hlsl/comp/globallycoherent.comp | 16 + .../frag/spec-constant-ternary.frag | 23 + .../asm/frag/unknown-depth-state.asm.frag | 22 + .../vert/uint-vertex-id-instance-id.asm.vert | 17 + .../frag/spec-constant-ternary.frag | 22 + .../shaders-msl/vert/set_builtin_in_func.vert | 19 + .../opt/shaders-msl/vert/texture_buffer.vert | 10 +- .../frag/switch-label-shared-block.asm.frag | 33 + .../asm/frag/unknown-depth-state.asm.vk.frag | 13 + .../frag/unknown-depth-state.asm.vk.frag.vk | 14 + .../geom/store-uint-layer.invalid.asm.geom | 41 + .../vert/uint-vertex-id-instance-id.asm.vert | 9 + .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + .../frag/spec-constant-ternary.vk.frag.vk | 13 + .../asm/comp/control-flow-hints.asm.comp | 41 + .../asm/frag/unknown-depth-state.asm.frag | 41 + .../vert/uint-vertex-id-instance-id.asm.vert | 37 + .../shaders-hlsl/comp/globallycoherent.comp | 18 + .../frag/spec-constant-ternary.frag | 23 + .../vert/functions_nested.vert | 20 +- .../asm/frag/unknown-depth-state.asm.frag | 34 + .../vert/uint-vertex-id-instance-id.asm.vert | 28 + .../frag/spec-constant-ternary.frag | 22 + .../shaders-msl/vert/set_builtin_in_func.vert | 26 + .../shaders-msl/vert/texture_buffer.vert | 10 +- .../aliased-entry-point-names.asm.multi.json | 49 ++ .../comp/struct-layout.comp.json | 64 ++ .../comp/struct-packing.comp.json | 474 ++++++++++++ ...mbined-texture-sampler-shadow.vk.frag.json | 37 + .../combined-texture-sampler.vk.frag.json | 50 ++ .../image-load-store-uint-coord.asm.frag.json | 47 ++ .../frag/input-attachment-ms.vk.frag.json | 31 + .../frag/input-attachment.vk.frag.json | 31 + .../frag/push-constant.vk.frag.json | 46 ++ ...eparate-sampler-texture-array.vk.frag.json | 73 ++ .../frag/spec-constant.vk.frag.json | 71 ++ .../vert/read-from-row-major-array.vert.json | 61 ++ .../vert/texture_buffer.vert.json | 40 + .../frag/switch-label-shared-block.asm.frag | 33 + .../asm/frag/unknown-depth-state.asm.vk.frag | 23 + .../frag/unknown-depth-state.asm.vk.frag.vk | 24 + .../geom/store-uint-layer.invalid.asm.geom | 41 + .../vert/uint-vertex-id-instance-id.asm.vert | 18 + .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + .../frag/spec-constant-ternary.vk.frag.vk | 13 + .../asm/comp/control-flow-hints.asm.comp | 146 ++++ .../asm/frag/unknown-depth-state.asm.frag | 71 ++ .../vert/uint-vertex-id-instance-id.asm.vert | 65 ++ shaders-hlsl/comp/globallycoherent.comp | 25 + shaders-hlsl/frag/spec-constant-ternary.frag | 9 + .../asm/frag/unknown-depth-state.asm.frag | 71 ++ .../vert/uint-vertex-id-instance-id.asm.vert | 65 ++ shaders-msl/frag/spec-constant-ternary.frag | 9 + shaders-msl/vert/set_builtin_in_func.vert | 12 + .../asm/aliased-entry-point-names.asm.multi | 60 ++ shaders-reflection/comp/struct-layout.comp | 24 + shaders-reflection/comp/struct-packing.comp | 87 +++ .../combined-texture-sampler-shadow.vk.frag | 29 + .../frag/combined-texture-sampler.vk.frag | 47 ++ .../frag/image-load-store-uint-coord.asm.frag | 103 +++ .../frag/input-attachment-ms.vk.frag | 10 + .../frag/input-attachment.vk.frag | 11 + shaders-reflection/frag/push-constant.vk.frag | 16 + .../separate-sampler-texture-array.vk.frag | 42 ++ shaders-reflection/frag/spec-constant.vk.frag | 78 ++ .../vert/read-from-row-major-array.vert | 20 + shaders-reflection/vert/texture_buffer.vert | 10 + .../frag/switch-label-shared-block.asm.frag | 45 ++ .../asm/frag/unknown-depth-state.asm.vk.frag | 71 ++ .../geom/store-uint-layer.invalid.asm.geom | 130 ++++ .../vert/uint-vertex-id-instance-id.asm.vert | 65 ++ .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + spirv_common.hpp | 10 + spirv_cross.cpp | 180 ++++- spirv_cross.hpp | 41 +- spirv_glsl.cpp | 699 +++++++++++------- spirv_glsl.hpp | 19 +- spirv_hlsl.cpp | 162 ++-- spirv_hlsl.hpp | 7 +- spirv_msl.cpp | 186 +++-- spirv_msl.hpp | 5 + spirv_reflect.cpp | 573 ++++++++++++++ spirv_reflect.hpp | 72 ++ test_shaders.py | 163 +++- test_shaders.sh | 1 + update_test_shaders.sh | 1 + 94 files changed, 4898 insertions(+), 494 deletions(-) create mode 100644 reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/opt/shaders-hlsl/comp/globallycoherent.comp create mode 100644 reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/opt/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 reference/opt/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk create mode 100644 reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk create mode 100644 reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/shaders-hlsl/comp/globallycoherent.comp create mode 100644 reference/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 reference/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json create mode 100644 reference/shaders-reflection/comp/struct-layout.comp.json create mode 100644 reference/shaders-reflection/comp/struct-packing.comp.json create mode 100644 reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json create mode 100644 reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json create mode 100644 reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json create mode 100644 reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json create mode 100644 reference/shaders-reflection/frag/input-attachment.vk.frag.json create mode 100644 reference/shaders-reflection/frag/push-constant.vk.frag.json create mode 100644 reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json create mode 100644 reference/shaders-reflection/frag/spec-constant.vk.frag.json create mode 100644 reference/shaders-reflection/vert/read-from-row-major-array.vert.json create mode 100644 reference/shaders-reflection/vert/texture_buffer.vert.json create mode 100644 reference/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk create mode 100644 reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk create mode 100644 shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 shaders-hlsl/comp/globallycoherent.comp create mode 100644 shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 shaders-msl/frag/spec-constant-ternary.frag create mode 100644 shaders-msl/vert/set_builtin_in_func.vert create mode 100644 shaders-reflection/asm/aliased-entry-point-names.asm.multi create mode 100644 shaders-reflection/comp/struct-layout.comp create mode 100644 shaders-reflection/comp/struct-packing.comp create mode 100644 shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag create mode 100644 shaders-reflection/frag/combined-texture-sampler.vk.frag create mode 100644 shaders-reflection/frag/image-load-store-uint-coord.asm.frag create mode 100644 shaders-reflection/frag/input-attachment-ms.vk.frag create mode 100644 shaders-reflection/frag/input-attachment.vk.frag create mode 100644 shaders-reflection/frag/push-constant.vk.frag create mode 100644 shaders-reflection/frag/separate-sampler-texture-array.vk.frag create mode 100644 shaders-reflection/frag/spec-constant.vk.frag create mode 100644 shaders-reflection/vert/read-from-row-major-array.vert create mode 100644 shaders-reflection/vert/texture_buffer.vert create mode 100644 shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 shaders/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 spirv_reflect.cpp create mode 100644 spirv_reflect.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9226afd46f..2849b6957d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,10 @@ spirv_cross_add_library(spirv-cross-cpp spirv_cross_cpp STATIC ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.hpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.cpp) +spirv_cross_add_library(spirv-cross-reflect spirv_cross_reflect STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.cpp) + spirv_cross_add_library(spirv-cross-msl spirv_cross_msl STATIC ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp) @@ -110,7 +114,7 @@ target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options}) target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines}) install(TARGETS spirv-cross RUNTIME DESTINATION bin) -target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-hlsl spirv-cross-cpp spirv-cross-msl spirv-cross-util spirv-cross-core) +target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-hlsl spirv-cross-cpp spirv-cross-reflect spirv-cross-msl spirv-cross-util spirv-cross-core) target_link_libraries(spirv-cross-util spirv-cross-core) target_link_libraries(spirv-cross-glsl spirv-cross-core) target_link_libraries(spirv-cross-msl spirv-cross-glsl) diff --git a/README.md b/README.md index 5f92698602..d3c1a93a07 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader - Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL) - Convert SPIR-V to readable, usable and efficient HLSL - Convert SPIR-V to debuggable C++ [EXPERIMENTAL] + - Convert SPIR-V to a JSON reflection format [EXPERIMENTAL] - Reflection API to simplify the creation of Vulkan pipeline layouts - Reflection API to modify and tweak OpDecorations - Supports "all" of vertex, fragment, tessellation, geometry and compute shaders. diff --git a/main.cpp b/main.cpp index b309a82ac3..69bd135c1a 100644 --- a/main.cpp +++ b/main.cpp @@ -19,6 +19,7 @@ #include "spirv_glsl.hpp" #include "spirv_hlsl.hpp" #include "spirv_msl.hpp" +#include "spirv_reflect.hpp" #include #include #include @@ -149,6 +150,22 @@ struct CLIParser return val; } + // Return a string only if it's not prefixed with `--`, otherwise return the default value + const char *next_value_string(const char *default_value) + { + if (!argc) + { + return default_value; + } + + if (0 == strncmp("--", *argv, 2)) + { + return default_value; + } + + return next_string(); + } + const char *next_string() { if (!argc) @@ -461,6 +478,7 @@ struct CLIArguments bool fixup = false; bool yflip = false; bool sso = false; + bool support_nonzero_baseinstance = true; vector pls_in; vector pls_out; vector remaps; @@ -481,6 +499,7 @@ struct CLIArguments uint32_t iterations = 1; bool cpp = false; + string reflect; bool msl = false; bool hlsl = false; bool hlsl_compat = false; @@ -512,6 +531,7 @@ static void print_help() "\t[--msl]\n" "\t[--msl-version ]\n" "\t[--hlsl]\n" + "\t[--reflect]\n" "\t[--shader-model]\n" "\t[--hlsl-enable-compat]\n" "\t[--separate-shader-objects]\n" @@ -528,7 +548,8 @@ static void print_help() "\t[--rename-interface-variable ]\n" "\t[--set-hlsl-vertex-input-semantic ]\n" "\t[--rename-entry-point ]\n" - "\t[--combined-samplers-inherit-bindings]" + "\t[--combined-samplers-inherit-bindings]\n" + "\t[--no-support-nonzero-baseinstance]\n" "\n"); } @@ -663,6 +684,7 @@ static int main_inner(int argc, char *argv[]) cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; }); cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); }); cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; }); + cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); }); cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); }); cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility cbs.add("--msl", [&args](CLIParser &) { args.msl = true; }); @@ -737,6 +759,8 @@ static int main_inner(int argc, char *argv[]) cbs.add("--combined-samplers-inherit-bindings", [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; }); + cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; }); + cbs.default_handler = [&args](const char *value) { args.input = value; }; cbs.error_handler = [] { print_help(); }; @@ -757,8 +781,20 @@ static int main_inner(int argc, char *argv[]) return EXIT_FAILURE; } - unique_ptr compiler; + // Special case reflection because it has little to do with the path followed by code-outputting compilers + if (!args.reflect.empty()) + { + CompilerReflection compiler(read_spirv_file(args.input)); + compiler.set_format(args.reflect); + auto json = compiler.compile(); + if (args.output) + write_string_to_file(args.output, json.c_str()); + else + printf("%s", json.c_str()); + return EXIT_SUCCESS; + } + unique_ptr compiler; bool combined_image_samplers = false; bool build_dummy_sampler = false; @@ -895,6 +931,7 @@ static int main_inner(int argc, char *argv[]) opts.vulkan_semantics = args.vulkan_semantics; opts.vertex.fixup_clipspace = args.fixup; opts.vertex.flip_vert_y = args.yflip; + opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance; compiler->set_common_options(opts); // Set HLSL specific options. diff --git a/msvc/SPIRV-Cross.vcxproj b/msvc/SPIRV-Cross.vcxproj index 6040e2e116..d2287dcbe9 100644 --- a/msvc/SPIRV-Cross.vcxproj +++ b/msvc/SPIRV-Cross.vcxproj @@ -127,6 +127,7 @@ + @@ -138,6 +139,7 @@ + diff --git a/msvc/SPIRV-Cross.vcxproj.filters b/msvc/SPIRV-Cross.vcxproj.filters index f853c08b5b..3ff2c29a58 100644 --- a/msvc/SPIRV-Cross.vcxproj.filters +++ b/msvc/SPIRV-Cross.vcxproj.filters @@ -24,6 +24,9 @@ Source Files + + Source Files + Source Files @@ -50,6 +53,9 @@ Header Files + + Header Files + Header Files diff --git a/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..142ef5efa8 --- /dev/null +++ b/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,32 @@ +RWByteAddressBuffer bar : register(u0); +RWByteAddressBuffer foo : register(u1); + +void comp_main() +{ + [unroll] + for (int _135 = 0; _135 < 16; ) + { + bar.Store4(_135 * 16 + 0, asuint(asfloat(foo.Load4(_135 * 16 + 0)))); + _135++; + continue; + } + [loop] + for (int _136 = 0; _136 < 16; ) + { + bar.Store4((15 - _136) * 16 + 0, asuint(asfloat(foo.Load4(_136 * 16 + 0)))); + _136++; + continue; + } + [branch] + if (asfloat(bar.Load(160)) > 10.0f) + { + foo.Store4(320, asuint(5.0f.xxxx)); + } + foo.Store4(320, asuint(20.0f.xxxx)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..5b894de831 --- /dev/null +++ b/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,31 @@ +Texture2D uShadow : register(t0); +SamplerComparisonState _uShadow_sampler : register(s0); +Texture2D uTexture : register(t1); +SamplerComparisonState uSampler : register(s2); + +static float3 vUV; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uShadow.SampleCmp(_uShadow_sampler, vUV.xy, vUV.z) + uTexture.SampleCmp(uSampler, vUV.xy, vUV.z); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..0d1e8cc534 --- /dev/null +++ b/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(uint(gl_VertexIndex) + uint(gl_InstanceIndex)).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/reference/opt/shaders-hlsl/comp/globallycoherent.comp b/reference/opt/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..1637727deb --- /dev/null +++ b/reference/opt/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,16 @@ +globallycoherent RWByteAddressBuffer _29 : register(u3); +ByteAddressBuffer _33 : register(t2); +RWTexture2D uImageIn : register(u0); +globallycoherent RWTexture2D uImageOut : register(u1); + +void comp_main() +{ + uImageOut[int2(9, 7)] = uImageIn[int2(9, 7)].x; + _29.Store(0, asuint(asfloat(_33.Load(0)))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag b/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..12e0f5bd79 --- /dev/null +++ b/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,23 @@ +static const uint s = 10u; +static const bool _13 = (s > 20u); +static const uint _16 = _13 ? 30u : 50u; + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float(_16); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..e8a88623a2 --- /dev/null +++ b/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow [[texture(0)]], depth2d uTexture [[texture(1)]], sampler uShadowSmplr [[sampler(0)]], sampler uSampler [[sampler(2)]]) +{ + main0_out out = {}; + out.FragColor = uShadow.sample_compare(uShadowSmplr, in.vUV.xy, in.vUV.z) + uTexture.sample_compare(uSampler, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..d453aadef0 --- /dev/null +++ b/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + out.gl_Position = float4(float(gl_VertexIndex + gl_InstanceIndex)); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/spec-constant-ternary.frag b/reference/opt/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..5ab6b4fcb1 --- /dev/null +++ b/reference/opt/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint s_tmp [[function_constant(0)]]; +constant uint s = is_function_constant_defined(s_tmp) ? s_tmp : 10u; +constant bool _13 = (s > 20u); +constant uint _16 = _13 ? 30u : 50u; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float(_16); + return out; +} + diff --git a/reference/opt/shaders-msl/vert/set_builtin_in_func.vert b/reference/opt/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..51a858af1e --- /dev/null +++ b/reference/opt/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_PointSize = 1.0; + out.gl_Position = float4(out.gl_PointSize); + return out; +} + diff --git a/reference/opt/shaders-msl/vert/texture_buffer.vert b/reference/opt/shaders-msl/vert/texture_buffer.vert index f7bcb7918b..c45d298134 100644 --- a/reference/opt/shaders-msl/vert/texture_buffer.vert +++ b/reference/opt/shaders-msl/vert/texture_buffer.vert @@ -1,3 +1,5 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + #include #include @@ -8,10 +10,16 @@ struct main0_out float4 gl_Position [[position]]; }; +// Returns 2D texture coords corresponding to 1D texel buffer coords +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + vertex main0_out main0(texture2d uSamp [[texture(4)]], texture2d uSampo [[texture(5)]]) { main0_out out = {}; - out.gl_Position = uSamp.read(uint2(10, 0)) + uSampo.read(uint2(100, 0)); + out.gl_Position = uSamp.read(spvTexelBufferCoord(10)) + uSampo.read(spvTexelBufferCoord(100)); return out; } diff --git a/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag b/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..ade9044e35 --- /dev/null +++ b/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,33 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) flat in mediump int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + highp float _19; + switch (vIndex) + { + case 0: + case 2: + { + _19 = 1.0; + break; + } + case 1: + default: + { + _19 = 3.0; + break; + } + case 8: + { + _19 = 8.0; + break; + } + } + FragColor = _19; +} + diff --git a/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..6953ec61d0 --- /dev/null +++ b/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(binding = 0) uniform sampler2DShadow uShadow; +uniform sampler2DShadow SPIRV_Cross_CombineduTextureuSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(uShadow, vec3(vUV.xy, vUV.z)) + texture(SPIRV_Cross_CombineduTextureuSampler, vec3(vUV.xy, vUV.z)); +} + diff --git a/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000000..2f997036f5 --- /dev/null +++ b/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DShadow uShadow; +layout(set = 0, binding = 1) uniform texture2D uTexture; +layout(set = 0, binding = 2) uniform samplerShadow uSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(uShadow, vec3(vUV.xy, vUV.z)) + texture(sampler2DShadow(uTexture, uSampler), vec3(vUV.xy, vUV.z)); +} + diff --git a/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..c768d5da86 --- /dev/null +++ b/reference/opt/shaders/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,41 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +struct VertexOutput +{ + vec4 pos; +}; + +struct GeometryOutput +{ + vec4 pos; + uint layer; +}; + +void _main(VertexOutput _input[3], GeometryOutput stream) +{ + GeometryOutput _output; + _output.layer = 1u; + for (int v = 0; v < 3; v++) + { + _output.pos = _input[v].pos; + gl_Position = _output.pos; + gl_Layer = int(_output.layer); + EmitVertex(); + } + EndPrimitive(); +} + +void main() +{ + VertexOutput _input[3]; + _input[0].pos = gl_in[0].gl_Position; + _input[1].pos = gl_in[1].gl_Position; + _input[2].pos = gl_in[2].gl_Position; + VertexOutput param[3] = _input; + GeometryOutput param_1; + _main(param, param_1); + GeometryOutput stream = param_1; +} + diff --git a/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..c25e9bbe5b --- /dev/null +++ b/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,9 @@ +#version 450 + +uniform int SPIRV_Cross_BaseInstance; + +void main() +{ + gl_Position = vec4(float(uint(gl_VertexID) + uint((gl_InstanceID + SPIRV_Cross_BaseInstance)))); +} + diff --git a/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..91b0331b79 --- /dev/null +++ b/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float((10u > 20u) ? 30u : 50u); +} + diff --git a/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000000..59d3b99b9c --- /dev/null +++ b/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(constant_id = 0) const uint s = 10u; +const bool _13 = (s > 20u); +const uint _16 = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(_16); +} + diff --git a/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..9700100348 --- /dev/null +++ b/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,41 @@ +RWByteAddressBuffer bar : register(u0); +RWByteAddressBuffer foo : register(u1); + +void _main() +{ + [unroll] + for (int i = 0; i < 16; i++) + { + bar.Store4(i * 16 + 0, asuint(asfloat(foo.Load4(i * 16 + 0)))); + } + [loop] + for (int i_1 = 0; i_1 < 16; i_1++) + { + bar.Store4((15 - i_1) * 16 + 0, asuint(asfloat(foo.Load4(i_1 * 16 + 0)))); + } + float v = asfloat(bar.Load(160)); + float w = asfloat(foo.Load(160)); + [branch] + if (v > 10.0f) + { + foo.Store4(320, asuint(5.0f.xxxx)); + } + float value = 20.0f; + [flatten] + if (w > 40.0f) + { + value = 20.0f; + } + foo.Store4(320, asuint(value.xxxx)); +} + +void comp_main() +{ + _main(); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..027dd00c5a --- /dev/null +++ b/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,41 @@ +Texture2D uShadow : register(t0); +SamplerComparisonState _uShadow_sampler : register(s0); +Texture2D uTexture : register(t1); +SamplerComparisonState uSampler : register(s2); + +static float3 vUV; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float sample_combined() +{ + return uShadow.SampleCmp(_uShadow_sampler, vUV.xy, vUV.z); +} + +float sample_separate() +{ + return uTexture.SampleCmp(uSampler, vUV.xy, vUV.z); +} + +void frag_main() +{ + FragColor = sample_combined() + sample_separate(); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..a18c6e7056 --- /dev/null +++ b/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,37 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +float4 _main(uint vid, uint iid) +{ + return float(vid + iid).xxxx; +} + +void vert_main() +{ + uint vid = uint(gl_VertexIndex); + uint iid = uint(gl_InstanceIndex); + uint param = vid; + uint param_1 = iid; + gl_Position = _main(param, param_1); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/reference/shaders-hlsl/comp/globallycoherent.comp b/reference/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..69886256f8 --- /dev/null +++ b/reference/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,18 @@ +globallycoherent RWByteAddressBuffer _29 : register(u3); +ByteAddressBuffer _33 : register(t2); +RWTexture2D uImageIn : register(u0); +globallycoherent RWTexture2D uImageOut : register(u1); + +void comp_main() +{ + int2 coord = int2(9, 7); + float4 indata = uImageIn[coord].xxxx; + uImageOut[coord] = indata.x; + _29.Store(0, asuint(asfloat(_33.Load(0)))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/reference/shaders-hlsl/frag/spec-constant-ternary.frag b/reference/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..12e0f5bd79 --- /dev/null +++ b/reference/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,23 @@ +static const uint s = 10u; +static const bool _13 = (s > 20u); +static const uint _16 = _13 ? 30u : 50u; + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float(_16); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/reference/shaders-msl-no-opt/vert/functions_nested.vert b/reference/shaders-msl-no-opt/vert/functions_nested.vert index 43a508613b..037ead3922 100644 --- a/reference/shaders-msl-no-opt/vert/functions_nested.vert +++ b/reference/shaders-msl-no-opt/vert/functions_nested.vert @@ -36,6 +36,12 @@ struct main0_out float4 gl_Position [[position]]; }; +// Returns 2D texture coords corresponding to 1D texel buffer coords +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + attr_desc fetch_desc(thread const int& location, constant VertexBuffer& v_227) { int attribute_flags = v_227.input_attributes[location].w; @@ -76,10 +82,10 @@ float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thr { int _131 = first_byte; first_byte = _131 + 1; - tmp.x = input_stream.read(uint2(_131, 0)).x; + tmp.x = input_stream.read(spvTexelBufferCoord(_131)).x; int _138 = first_byte; first_byte = _138 + 1; - tmp.y = input_stream.read(uint2(_138, 0)).x; + tmp.y = input_stream.read(spvTexelBufferCoord(_138)).x; uint4 param = tmp; int param_1 = desc.swap_bytes; result[n] = float(get_bits(param, param_1)); @@ -89,16 +95,16 @@ float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thr { int _156 = first_byte; first_byte = _156 + 1; - tmp.x = input_stream.read(uint2(_156, 0)).x; + tmp.x = input_stream.read(spvTexelBufferCoord(_156)).x; int _163 = first_byte; first_byte = _163 + 1; - tmp.y = input_stream.read(uint2(_163, 0)).x; + tmp.y = input_stream.read(spvTexelBufferCoord(_163)).x; int _170 = first_byte; first_byte = _170 + 1; - tmp.z = input_stream.read(uint2(_170, 0)).x; + tmp.z = input_stream.read(spvTexelBufferCoord(_170)).x; int _177 = first_byte; first_byte = _177 + 1; - tmp.w = input_stream.read(uint2(_177, 0)).x; + tmp.w = input_stream.read(spvTexelBufferCoord(_177)).x; uint4 param_2 = tmp; int param_3 = desc.swap_bytes; result[n] = as_type(get_bits(param_2, param_3)); @@ -108,7 +114,7 @@ float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thr { int _195 = first_byte; first_byte = _195 + 1; - result[n] = float(input_stream.read(uint2(_195, 0)).x); + result[n] = float(input_stream.read(spvTexelBufferCoord(_195)).x); reverse_order = desc.swap_bytes != 0; break; } diff --git a/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..005af22e3a --- /dev/null +++ b/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,34 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +float sample_combined(thread float3& vUV, thread depth2d uShadow, thread const sampler uShadowSmplr) +{ + return uShadow.sample_compare(uShadowSmplr, vUV.xy, vUV.z); +} + +float sample_separate(thread float3& vUV, thread depth2d uTexture, thread sampler uSampler) +{ + return uTexture.sample_compare(uSampler, vUV.xy, vUV.z); +} + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow [[texture(0)]], depth2d uTexture [[texture(1)]], sampler uShadowSmplr [[sampler(0)]], sampler uSampler [[sampler(2)]]) +{ + main0_out out = {}; + out.FragColor = sample_combined(in.vUV, uShadow, uShadowSmplr) + sample_separate(in.vUV, uTexture, uSampler); + return out; +} + diff --git a/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..89ca17f98b --- /dev/null +++ b/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +float4 _main(thread const uint& vid, thread const uint& iid) +{ + return float4(float(vid + iid)); +} + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + uint vid = gl_VertexIndex; + uint iid = gl_InstanceIndex; + uint param = vid; + uint param_1 = iid; + out.gl_Position = _main(param, param_1); + return out; +} + diff --git a/reference/shaders-msl/frag/spec-constant-ternary.frag b/reference/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..5ab6b4fcb1 --- /dev/null +++ b/reference/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint s_tmp [[function_constant(0)]]; +constant uint s = is_function_constant_defined(s_tmp) ? s_tmp : 10u; +constant bool _13 = (s > 20u); +constant uint _16 = _13 ? 30u : 50u; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float(_16); + return out; +} + diff --git a/reference/shaders-msl/vert/set_builtin_in_func.vert b/reference/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..2952748dc0 --- /dev/null +++ b/reference/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +void write_outblock(thread float4& gl_Position, thread float& gl_PointSize) +{ + gl_PointSize = 1.0; + gl_Position = float4(gl_PointSize); +} + +vertex main0_out main0() +{ + main0_out out = {}; + write_outblock(out.gl_Position, out.gl_PointSize); + return out; +} + diff --git a/reference/shaders-msl/vert/texture_buffer.vert b/reference/shaders-msl/vert/texture_buffer.vert index f7bcb7918b..c45d298134 100644 --- a/reference/shaders-msl/vert/texture_buffer.vert +++ b/reference/shaders-msl/vert/texture_buffer.vert @@ -1,3 +1,5 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + #include #include @@ -8,10 +10,16 @@ struct main0_out float4 gl_Position [[position]]; }; +// Returns 2D texture coords corresponding to 1D texel buffer coords +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + vertex main0_out main0(texture2d uSamp [[texture(4)]], texture2d uSampo [[texture(5)]]) { main0_out out = {}; - out.gl_Position = uSamp.read(uint2(10, 0)) + uSampo.read(uint2(100, 0)); + out.gl_Position = uSamp.read(spvTexelBufferCoord(10)) + uSampo.read(spvTexelBufferCoord(100)); return out; } diff --git a/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json b/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json new file mode 100644 index 0000000000..45adf50b14 --- /dev/null +++ b/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json @@ -0,0 +1,49 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + }, + { + "name" : "main2", + "mode" : "vert" + }, + { + "name" : "main", + "mode" : "frag" + }, + { + "name" : "main2", + "mode" : "frag" + } + ], + "types" : { + "_8" : { + "name" : "_8", + "members" : [ + { + "name" : "_m0", + "type" : "vec4" + }, + { + "name" : "_m1", + "type" : "float" + }, + { + "name" : "_m2", + "type" : "float", + "array" : [ + 1 + ] + }, + { + "name" : "_m3", + "type" : "float", + "array" : [ + 1 + ] + } + ] + } + } +} \ No newline at end of file diff --git a/reference/shaders-reflection/comp/struct-layout.comp.json b/reference/shaders-reflection/comp/struct-layout.comp.json new file mode 100644 index 0000000000..3004454b80 --- /dev/null +++ b/reference/shaders-reflection/comp/struct-layout.comp.json @@ -0,0 +1,64 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp" + } + ], + "types" : { + "_19" : { + "name" : "Foo", + "members" : [ + { + "name" : "m", + "type" : "mat4", + "offset" : 0 + } + ] + }, + "_21" : { + "name" : "SSBO2", + "members" : [ + { + "name" : "out_data", + "type" : "_19", + "array" : [ + 0 + ], + "offset" : 0 + } + ] + }, + "_28" : { + "name" : "SSBO", + "members" : [ + { + "name" : "in_data", + "type" : "_19", + "array" : [ + 0 + ], + "offset" : 0 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_21", + "name" : "SSBO2", + "writeonly" : true, + "block_size" : 0, + "set" : 0, + "binding" : 1 + }, + { + "type" : "_28", + "name" : "SSBO", + "readonly" : true, + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/comp/struct-packing.comp.json b/reference/shaders-reflection/comp/struct-packing.comp.json new file mode 100644 index 0000000000..22a41584d9 --- /dev/null +++ b/reference/shaders-reflection/comp/struct-packing.comp.json @@ -0,0 +1,474 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp" + } + ], + "types" : { + "_11" : { + "name" : "S0", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_14" : { + "name" : "S1", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 12 + } + ] + }, + "_17" : { + "name" : "S2", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_19" : { + "name" : "S3", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_20" : { + "name" : "S4", + "members" : [ + { + "name" : "c", + "type" : "vec2", + "offset" : 0 + } + ] + }, + "_23" : { + "name" : "Content", + "members" : [ + { + "name" : "m0s", + "type" : "_11", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "m1s", + "type" : "_14", + "array" : [ + 1 + ], + "offset" : 16 + }, + { + "name" : "m2s", + "type" : "_17", + "array" : [ + 1 + ], + "offset" : 32 + }, + { + "name" : "m0", + "type" : "_11", + "offset" : 64 + }, + { + "name" : "m1", + "type" : "_14", + "offset" : 80 + }, + { + "name" : "m2", + "type" : "_17", + "offset" : 96 + }, + { + "name" : "m3", + "type" : "_19", + "offset" : 128 + }, + { + "name" : "m4", + "type" : "float", + "offset" : 144 + }, + { + "name" : "m3s", + "type" : "_20", + "array" : [ + 8 + ], + "offset" : 152 + } + ] + }, + "_36" : { + "name" : "SSBO1", + "members" : [ + { + "name" : "content", + "type" : "_23", + "offset" : 0 + }, + { + "name" : "content1", + "type" : "_23", + "array" : [ + 2 + ], + "offset" : 224 + }, + { + "name" : "content2", + "type" : "_23", + "offset" : 672 + }, + { + "name" : "m0", + "type" : "mat2", + "offset" : 896 + }, + { + "name" : "m1", + "type" : "mat2", + "offset" : 912 + }, + { + "name" : "m2", + "type" : "mat2x3", + "array" : [ + 4 + ], + "offset" : 928 + }, + { + "name" : "m3", + "type" : "mat3x2", + "offset" : 1056 + }, + { + "name" : "m4", + "type" : "mat2", + "row_major" : true, + "offset" : 1080 + }, + { + "name" : "m5", + "type" : "mat2", + "row_major" : true, + "array" : [ + 9 + ], + "offset" : 1096 + }, + { + "name" : "m6", + "type" : "mat2x3", + "row_major" : true, + "array" : [ + 2, + 4 + ], + "offset" : 1240 + }, + { + "name" : "m7", + "type" : "mat3x2", + "row_major" : true, + "offset" : 1440 + }, + { + "name" : "array", + "type" : "float", + "array" : [ + 0 + ], + "offset" : 1472 + } + ] + }, + "_42" : { + "name" : "S0", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_44" : { + "name" : "S1", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 12 + } + ] + }, + "_47" : { + "name" : "S2", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_49" : { + "name" : "S3", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_50" : { + "name" : "S4", + "members" : [ + { + "name" : "c", + "type" : "vec2", + "offset" : 0 + } + ] + }, + "_52" : { + "name" : "Content", + "members" : [ + { + "name" : "m0s", + "type" : "_42", + "array" : [ + 1 + ], + "offset" : 0 + }, + { + "name" : "m1s", + "type" : "_44", + "array" : [ + 1 + ], + "offset" : 32 + }, + { + "name" : "m2s", + "type" : "_47", + "array" : [ + 1 + ], + "offset" : 48 + }, + { + "name" : "m0", + "type" : "_42", + "offset" : 80 + }, + { + "name" : "m1", + "type" : "_44", + "offset" : 112 + }, + { + "name" : "m2", + "type" : "_47", + "offset" : 128 + }, + { + "name" : "m3", + "type" : "_49", + "offset" : 160 + }, + { + "name" : "m4", + "type" : "float", + "offset" : 176 + }, + { + "name" : "m3s", + "type" : "_50", + "array" : [ + 8 + ], + "offset" : 192 + } + ] + }, + "_59" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "content", + "type" : "_52", + "offset" : 0 + }, + { + "name" : "content1", + "type" : "_52", + "array" : [ + 2 + ], + "offset" : 320 + }, + { + "name" : "content2", + "type" : "_52", + "offset" : 960 + }, + { + "name" : "m0", + "type" : "mat2", + "offset" : 1280 + }, + { + "name" : "m1", + "type" : "mat2", + "offset" : 1312 + }, + { + "name" : "m2", + "type" : "mat2x3", + "array" : [ + 4 + ], + "offset" : 1344 + }, + { + "name" : "m3", + "type" : "mat3x2", + "offset" : 1472 + }, + { + "name" : "m4", + "type" : "mat2", + "row_major" : true, + "offset" : 1520 + }, + { + "name" : "m5", + "type" : "mat2", + "row_major" : true, + "array" : [ + 9 + ], + "offset" : 1552 + }, + { + "name" : "m6", + "type" : "mat2x3", + "row_major" : true, + "array" : [ + 2, + 4 + ], + "offset" : 1840 + }, + { + "name" : "m7", + "type" : "mat3x2", + "row_major" : true, + "offset" : 2224 + }, + { + "name" : "array", + "type" : "float", + "array" : [ + 0 + ], + "offset" : 2256 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_36", + "name" : "SSBO1", + "restrict" : true, + "block_size" : 1472, + "set" : 0, + "binding" : 1 + }, + { + "type" : "_59", + "name" : "SSBO0", + "restrict" : true, + "block_size" : 2256, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json b/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json new file mode 100644 index 0000000000..5b4d3c6f7b --- /dev/null +++ b/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json @@ -0,0 +1,37 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "float", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uDepth", + "set" : 0, + "binding" : 2 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler", + "set" : 0, + "binding" : 0 + }, + { + "type" : "sampler", + "name" : "uSampler1", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json b/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json new file mode 100644 index 0000000000..8b6a184299 --- /dev/null +++ b/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json @@ -0,0 +1,50 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "inputs" : [ + { + "type" : "vec2", + "name" : "vTex", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uTexture0", + "set" : 0, + "binding" : 2 + }, + { + "type" : "texture2D", + "name" : "uTexture1", + "set" : 0, + "binding" : 3 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler0", + "set" : 0, + "binding" : 0 + }, + { + "type" : "sampler", + "name" : "uSampler1", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json b/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json new file mode 100644 index 0000000000..527ea2bfee --- /dev/null +++ b/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json @@ -0,0 +1,47 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "_entryPointOutput", + "location" : 0 + } + ], + "textures" : [ + { + "type" : "sampler2D", + "name" : "ROIm", + "set" : 0, + "binding" : 1 + } + ], + "separate_images" : [ + { + "type" : "samplerBuffer", + "name" : "ROBuf", + "set" : 0, + "binding" : 0 + } + ], + "images" : [ + { + "type" : "image2D", + "name" : "RWIm", + "set" : 0, + "binding" : 1, + "format" : "rgba32f" + }, + { + "type" : "imageBuffer", + "name" : "RWBuf", + "set" : 0, + "binding" : 0, + "format" : "rgba32f" + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json b/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json new file mode 100644 index 0000000000..5f381911ac --- /dev/null +++ b/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json @@ -0,0 +1,31 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "subpass_inputs" : [ + { + "type" : "subpassInputMS", + "name" : "uSubpass0", + "set" : 0, + "binding" : 0, + "input_attachment_index" : 0 + }, + { + "type" : "subpassInputMS", + "name" : "uSubpass1", + "set" : 0, + "binding" : 1, + "input_attachment_index" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/input-attachment.vk.frag.json b/reference/shaders-reflection/frag/input-attachment.vk.frag.json new file mode 100644 index 0000000000..16ae6a4683 --- /dev/null +++ b/reference/shaders-reflection/frag/input-attachment.vk.frag.json @@ -0,0 +1,31 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "subpass_inputs" : [ + { + "type" : "subpassInput", + "name" : "uSubpass0", + "set" : 0, + "binding" : 0, + "input_attachment_index" : 0 + }, + { + "type" : "subpassInput", + "name" : "uSubpass1", + "set" : 0, + "binding" : 1, + "input_attachment_index" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/push-constant.vk.frag.json b/reference/shaders-reflection/frag/push-constant.vk.frag.json new file mode 100644 index 0000000000..f72a8fd654 --- /dev/null +++ b/reference/shaders-reflection/frag/push-constant.vk.frag.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_13" : { + "name" : "PushConstants", + "members" : [ + { + "name" : "value0", + "type" : "vec4", + "offset" : 0 + }, + { + "name" : "value1", + "type" : "vec4", + "offset" : 16 + } + ] + } + }, + "inputs" : [ + { + "type" : "vec4", + "name" : "vColor", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "push_constants" : [ + { + "type" : "_13", + "name" : "push", + "push_constant" : true + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json b/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json new file mode 100644 index 0000000000..9216d93e5d --- /dev/null +++ b/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json @@ -0,0 +1,73 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "inputs" : [ + { + "type" : "vec2", + "name" : "vTex", + "location" : 0 + }, + { + "type" : "vec3", + "name" : "vTex3", + "location" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uTexture", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 1 + }, + { + "type" : "texture2DArray", + "name" : "uTextureArray", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 4 + }, + { + "type" : "textureCube", + "name" : "uTextureCube", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 3 + }, + { + "type" : "texture3D", + "name" : "uTexture3D", + "array" : [ + 4 + ], + "set" : 0, + "binding" : 2 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler", + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/frag/spec-constant.vk.frag.json b/reference/shaders-reflection/frag/spec-constant.vk.frag.json new file mode 100644 index 0000000000..0add298666 --- /dev/null +++ b/reference/shaders-reflection/frag/spec-constant.vk.frag.json @@ -0,0 +1,71 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_137" : { + "name" : "Foo", + "members" : [ + { + "name" : "elems", + "type" : "float", + "array" : [ + 135 + ] + } + ] + } + }, + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "specialization_constants" : [ + { + "id" : 1, + "type" : "float", + "default_value" : 1.5 + }, + { + "id" : 2, + "type" : "float", + "default_value" : 2.5 + }, + { + "id" : 3, + "type" : "int", + "default_value" : 3 + }, + { + "id" : 4, + "type" : "int", + "default_value" : 4 + }, + { + "id" : 5, + "type" : "uint", + "default_value" : 5 + }, + { + "id" : 6, + "type" : "uint", + "default_value" : 6 + }, + { + "id" : 7, + "type" : "bool", + "default_value" : false + }, + { + "id" : 8, + "type" : "bool", + "default_value" : true + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/vert/read-from-row-major-array.vert.json b/reference/shaders-reflection/vert/read-from-row-major-array.vert.json new file mode 100644 index 0000000000..d92fb67fb5 --- /dev/null +++ b/reference/shaders-reflection/vert/read-from-row-major-array.vert.json @@ -0,0 +1,61 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_89" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + } + ] + }, + "_102" : { + "name" : "Block", + "members" : [ + { + "name" : "var", + "type" : "mat2x3", + "row_major" : true, + "array" : [ + 4, + 3 + ], + "offset" : 0 + } + ] + } + }, + "inputs" : [ + { + "type" : "vec4", + "name" : "a_position", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "float", + "name" : "v_vtxResult", + "location" : 0 + } + ], + "ubos" : [ + { + "type" : "_102", + "name" : "Block", + "block_size" : 576, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/reference/shaders-reflection/vert/texture_buffer.vert.json b/reference/shaders-reflection/vert/texture_buffer.vert.json new file mode 100644 index 0000000000..3c69e24cbc --- /dev/null +++ b/reference/shaders-reflection/vert/texture_buffer.vert.json @@ -0,0 +1,40 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_8" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + } + ] + } + }, + "textures" : [ + { + "type" : "samplerBuffer", + "name" : "uSamp", + "set" : 0, + "binding" : 4 + } + ], + "images" : [ + { + "type" : "imageBuffer", + "name" : "uSampo", + "set" : 0, + "binding" : 5, + "format" : "rgba32f" + } + ] +} \ No newline at end of file diff --git a/reference/shaders/asm/frag/switch-label-shared-block.asm.frag b/reference/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..ade9044e35 --- /dev/null +++ b/reference/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,33 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) flat in mediump int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + highp float _19; + switch (vIndex) + { + case 0: + case 2: + { + _19 = 1.0; + break; + } + case 1: + default: + { + _19 = 3.0; + break; + } + case 8: + { + _19 = 8.0; + break; + } + } + FragColor = _19; +} + diff --git a/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..7729d30c09 --- /dev/null +++ b/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(binding = 0) uniform sampler2DShadow uShadow; +uniform sampler2DShadow SPIRV_Cross_CombineduTextureuSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +float sample_combined() +{ + return texture(uShadow, vec3(vUV.xy, vUV.z)); +} + +float sample_separate() +{ + return texture(SPIRV_Cross_CombineduTextureuSampler, vec3(vUV.xy, vUV.z)); +} + +void main() +{ + FragColor = sample_combined() + sample_separate(); +} + diff --git a/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000000..711fa27763 --- /dev/null +++ b/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk @@ -0,0 +1,24 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DShadow uShadow; +layout(set = 0, binding = 1) uniform texture2D uTexture; +layout(set = 0, binding = 2) uniform samplerShadow uSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +float sample_combined() +{ + return texture(uShadow, vec3(vUV.xy, vUV.z)); +} + +float sample_separate() +{ + return texture(sampler2DShadow(uTexture, uSampler), vec3(vUV.xy, vUV.z)); +} + +void main() +{ + FragColor = sample_combined() + sample_separate(); +} + diff --git a/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..c768d5da86 --- /dev/null +++ b/reference/shaders/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,41 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +struct VertexOutput +{ + vec4 pos; +}; + +struct GeometryOutput +{ + vec4 pos; + uint layer; +}; + +void _main(VertexOutput _input[3], GeometryOutput stream) +{ + GeometryOutput _output; + _output.layer = 1u; + for (int v = 0; v < 3; v++) + { + _output.pos = _input[v].pos; + gl_Position = _output.pos; + gl_Layer = int(_output.layer); + EmitVertex(); + } + EndPrimitive(); +} + +void main() +{ + VertexOutput _input[3]; + _input[0].pos = gl_in[0].gl_Position; + _input[1].pos = gl_in[1].gl_Position; + _input[2].pos = gl_in[2].gl_Position; + VertexOutput param[3] = _input; + GeometryOutput param_1; + _main(param, param_1); + GeometryOutput stream = param_1; +} + diff --git a/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..31f13bd777 --- /dev/null +++ b/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,18 @@ +#version 450 + +uniform int SPIRV_Cross_BaseInstance; + +vec4 _main(uint vid, uint iid) +{ + return vec4(float(vid + iid)); +} + +void main() +{ + uint vid = uint(gl_VertexID); + uint iid = uint((gl_InstanceID + SPIRV_Cross_BaseInstance)); + uint param = vid; + uint param_1 = iid; + gl_Position = _main(param, param_1); +} + diff --git a/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..91b0331b79 --- /dev/null +++ b/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float((10u > 20u) ? 30u : 50u); +} + diff --git a/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000000..59d3b99b9c --- /dev/null +++ b/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(constant_id = 0) const uint s = 10u; +const bool _13 = (s > 20u); +const uint _16 = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(_16); +} + diff --git a/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000000..74a15955c2 --- /dev/null +++ b/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,146 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 85 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %i "i" + OpName %bar "bar" + OpMemberName %bar 0 "@data" + OpName %bar_0 "bar" + OpName %foo "foo" + OpName %i_0 "i" + OpName %v "v" + OpName %w "w" + OpName %value "value" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %bar 0 Offset 0 + OpDecorate %bar BufferBlock + OpDecorate %bar_0 DescriptorSet 0 + OpDecorate %bar_0 Binding 0 + OpDecorate %foo DescriptorSet 0 + OpDecorate %foo Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %bar = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_bar = OpTypePointer Uniform %bar + %bar_0 = OpVariable %_ptr_Uniform_bar Uniform + %foo = OpVariable %_ptr_Uniform_bar Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %int_15 = OpConstant %int 15 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_10 = OpConstant %float 10 + %int_20 = OpConstant %int 20 + %float_5 = OpConstant %float 5 + %72 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %float_20 = OpConstant %float 20 + %float_40 = OpConstant %float 40 + %main = OpFunction %void None %3 + %5 = OpLabel + %84 = OpFunctionCall %void %_main_ + OpReturn + OpFunctionEnd + %_main_ = OpFunction %void None %3 + %7 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %i_0 = OpVariable %_ptr_Function_int Function + %v = OpVariable %_ptr_Function_float Function + %w = OpVariable %_ptr_Function_float Function + %value = OpVariable %_ptr_Function_float Function + OpStore %i %int_0 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 Unroll + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %int %i + %20 = OpSLessThan %bool %17 %int_16 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %27 = OpLoad %int %i + %29 = OpLoad %int %i + %31 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %29 + %32 = OpLoad %v4float %31 + %33 = OpAccessChain %_ptr_Uniform_v4float %bar_0 %int_0 %27 + OpStore %33 %32 + OpBranch %15 + %15 = OpLabel + %34 = OpLoad %int %i + %36 = OpIAdd %int %34 %int_1 + OpStore %i %36 + OpBranch %12 + %14 = OpLabel + OpStore %i_0 %int_0 + OpBranch %38 + %38 = OpLabel + OpLoopMerge %40 %41 DontUnroll + OpBranch %42 + %42 = OpLabel + %43 = OpLoad %int %i_0 + %44 = OpSLessThan %bool %43 %int_16 + OpBranchConditional %44 %39 %40 + %39 = OpLabel + %46 = OpLoad %int %i_0 + %47 = OpISub %int %int_15 %46 + %48 = OpLoad %int %i_0 + %49 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %48 + %50 = OpLoad %v4float %49 + %51 = OpAccessChain %_ptr_Uniform_v4float %bar_0 %int_0 %47 + OpStore %51 %50 + OpBranch %41 + %41 = OpLabel + %52 = OpLoad %int %i_0 + %53 = OpIAdd %int %52 %int_1 + OpStore %i_0 %53 + OpBranch %38 + %40 = OpLabel + %60 = OpAccessChain %_ptr_Uniform_float %bar_0 %int_0 %int_10 %uint_0 + %61 = OpLoad %float %60 + OpStore %v %61 + %63 = OpAccessChain %_ptr_Uniform_float %foo %int_0 %int_10 %uint_0 + %64 = OpLoad %float %63 + OpStore %w %64 + %65 = OpLoad %float %v + %67 = OpFOrdGreaterThan %bool %65 %float_10 + OpSelectionMerge %69 DontFlatten + OpBranchConditional %67 %68 %69 + %68 = OpLabel + %73 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %int_20 + OpStore %73 %72 + OpBranch %69 + %69 = OpLabel + OpStore %value %float_20 + %76 = OpLoad %float %w + %78 = OpFOrdGreaterThan %bool %76 %float_40 + OpSelectionMerge %80 Flatten + OpBranchConditional %78 %79 %80 + %79 = OpLabel + OpStore %value %float_20 + OpBranch %80 + %80 = OpLabel + %81 = OpLoad %float %value + %82 = OpCompositeConstruct %v4float %81 %81 %81 %81 + %83 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %int_20 + OpStore %83 %82 + OpReturn + OpFunctionEnd diff --git a/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/shaders-hlsl/comp/globallycoherent.comp b/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000000..168b9404ea --- /dev/null +++ b/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,25 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(r32f, binding = 0) uniform readonly image2D uImageIn; +layout(r32f, binding = 1) uniform coherent writeonly image2D uImageOut; + +layout(set = 0, binding = 2) readonly buffer Foo +{ + float foo; +}; + +layout(set = 0, binding = 3) coherent writeonly buffer Bar +{ + float bar; +}; + +void main() +{ + ivec2 coord = ivec2(9, 7); + vec4 indata = imageLoad(uImageIn, coord); + imageStore(uImageOut, coord, indata); + + bar = foo; +} + diff --git a/shaders-hlsl/frag/spec-constant-ternary.frag b/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/shaders-msl/frag/spec-constant-ternary.frag b/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/shaders-msl/vert/set_builtin_in_func.vert b/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000000..dd991e3545 --- /dev/null +++ b/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,12 @@ +#version 450 + +void write_outblock() +{ + gl_PointSize = 1.0; + gl_Position = vec4(gl_PointSize); +} + +void main() +{ + write_outblock(); +} diff --git a/shaders-reflection/asm/aliased-entry-point-names.asm.multi b/shaders-reflection/asm/aliased-entry-point-names.asm.multi new file mode 100644 index 0000000000..d60cf3039c --- /dev/null +++ b/shaders-reflection/asm/aliased-entry-point-names.asm.multi @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpEntryPoint Vertex %main2 "main2" %_ + OpEntryPoint Fragment %main3 "main" %FragColor + OpEntryPoint Fragment %main4 "main2" %FragColor + OpSource GLSL 450 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %FragColor Location 0 + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v4floatptr = OpTypePointer Output %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %FragColor = OpVariable %v4floatptr Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_2 = OpConstant %float 2 + %18 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %6 = OpLabel + %20 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %20 %18 + OpReturn + OpFunctionEnd + %main3 = OpFunction %void None %3 + %7 = OpLabel + OpStore %FragColor %17 + OpReturn + OpFunctionEnd + %main4 = OpFunction %void None %3 + %8 = OpLabel + OpStore %FragColor %18 + OpReturn + OpFunctionEnd diff --git a/shaders-reflection/comp/struct-layout.comp b/shaders-reflection/comp/struct-layout.comp new file mode 100644 index 0000000000..5a2b7802df --- /dev/null +++ b/shaders-reflection/comp/struct-layout.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(std430, binding = 0) readonly buffer SSBO +{ + Foo in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + Foo out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident].m = in_data[ident].m * in_data[ident].m; +} + diff --git a/shaders-reflection/comp/struct-packing.comp b/shaders-reflection/comp/struct-packing.comp new file mode 100644 index 0000000000..d2ffbaef50 --- /dev/null +++ b/shaders-reflection/comp/struct-packing.comp @@ -0,0 +1,87 @@ +#version 450 + +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + S4 m3s[8]; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content = ssbo_140.content; +} + diff --git a/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag b/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000000..2fabb5ea8a --- /dev/null +++ b/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform texture2D uDepth; +layout(location = 0) out float FragColor; + +float samp2(texture2D t, mediump samplerShadow s) +{ + return texture(sampler2DShadow(t, s), vec3(1.0)); +} + +float samp3(texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(1.0)).x; +} + +float samp(texture2D t, mediump samplerShadow s, mediump sampler s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} diff --git a/shaders-reflection/frag/combined-texture-sampler.vk.frag b/shaders-reflection/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000000..b7de8d47e9 --- /dev/null +++ b/shaders-reflection/frag/combined-texture-sampler.vk.frag @@ -0,0 +1,47 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler0; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform mediump texture2D uTexture0; +layout(set = 0, binding = 3) uniform mediump texture2D uTexture1; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; + +vec4 sample_dual(mediump sampler samp, mediump texture2D tex) +{ + return texture(sampler2D(tex, samp), vTex); +} + +vec4 sample_global_tex(mediump sampler samp) +{ + vec4 a = texture(sampler2D(uTexture0, samp), vTex); + vec4 b = sample_dual(samp, uTexture1); + return a + b; +} + +vec4 sample_global_sampler(mediump texture2D tex) +{ + vec4 a = texture(sampler2D(tex, uSampler0), vTex); + vec4 b = sample_dual(uSampler1, tex); + return a + b; +} + +vec4 sample_duals() +{ + vec4 a = sample_dual(uSampler0, uTexture0); + vec4 b = sample_dual(uSampler1, uTexture1); + return a + b; +} + +void main() +{ + vec4 c0 = sample_duals(); + vec4 c1 = sample_global_tex(uSampler0); + vec4 c2 = sample_global_tex(uSampler1); + vec4 c3 = sample_global_sampler(uTexture0); + vec4 c4 = sample_global_sampler(uTexture1); + + FragColor = c0 + c1 + c2 + c3 + c4; +} diff --git a/shaders-reflection/frag/image-load-store-uint-coord.asm.frag b/shaders-reflection/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000000..a9bf1a7497 --- /dev/null +++ b/shaders-reflection/frag/image-load-store-uint-coord.asm.frag @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %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 %storeTemp "storeTemp" + OpName %RWIm "RWIm" + OpName %v "v" + OpName %RWBuf "RWBuf" + OpName %ROIm "ROIm" + OpName %ROBuf "ROBuf" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %RWIm DescriptorSet 0 + OpDecorate %RWIm Binding 1 + OpDecorate %RWBuf DescriptorSet 0 + OpDecorate %RWBuf Binding 0 + OpDecorate %ROIm DescriptorSet 0 + OpDecorate %ROIm Binding 1 + OpDecorate %ROBuf DescriptorSet 0 + OpDecorate %ROBuf Binding 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_10 = OpConstant %float 10 + %float_0_5 = OpConstant %float 0.5 + %float_8 = OpConstant %float 8 + %float_2 = OpConstant %float 2 + %17 = OpConstantComposite %v4float %float_10 %float_0_5 %float_8 %float_2 + %18 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %RWIm = OpVariable %_ptr_UniformConstant_18 UniformConstant + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_10 = OpConstant %uint 10 + %25 = OpConstantComposite %v2uint %uint_10 %uint_10 + %uint_30 = OpConstant %uint 30 + %30 = OpConstantComposite %v2uint %uint_30 %uint_30 + %32 = OpTypeImage %float Buffer 0 0 0 2 Rgba32f +%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 + %RWBuf = OpVariable %_ptr_UniformConstant_32 UniformConstant + %uint_80 = OpConstant %uint 80 + %38 = OpTypeImage %float 2D 0 0 0 1 Unknown + %SampledImage = OpTypeSampledImage %38 +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %SampledImage + %ROIm = OpVariable %_ptr_UniformConstant_38 UniformConstant + %uint_50 = OpConstant %uint 50 + %uint_60 = OpConstant %uint 60 + %44 = OpConstantComposite %v2uint %uint_50 %uint_60 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %50 = OpTypeImage %float Buffer 0 0 0 1 Rgba32f +%_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 + %ROBuf = OpVariable %_ptr_UniformConstant_50 UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %62 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %62 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %storeTemp = OpVariable %_ptr_Function_v4float Function + %v = OpVariable %_ptr_Function_v4float Function + OpStore %storeTemp %17 + %21 = OpLoad %18 %RWIm + %26 = OpLoad %v4float %storeTemp + OpImageWrite %21 %25 %26 + %28 = OpLoad %18 %RWIm + %31 = OpImageRead %v4float %28 %30 + OpStore %v %31 + %35 = OpLoad %32 %RWBuf + %37 = OpLoad %v4float %v + OpImageWrite %35 %uint_80 %37 + %41 = OpLoad %SampledImage %ROIm + %ROImage = OpImage %38 %41 + %47 = OpImageFetch %v4float %ROImage %44 Lod %int_0 + %48 = OpLoad %v4float %v + %49 = OpFAdd %v4float %48 %47 + OpStore %v %49 + %53 = OpLoad %50 %ROBuf + %54 = OpImageFetch %v4float %53 %uint_80 + %55 = OpLoad %v4float %v + %56 = OpFAdd %v4float %55 %54 + OpStore %v %56 + %57 = OpLoad %v4float %v + OpReturnValue %57 + OpFunctionEnd diff --git a/shaders-reflection/frag/input-attachment-ms.vk.frag b/shaders-reflection/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000000..e060738846 --- /dev/null +++ b/shaders-reflection/frag/input-attachment-ms.vk.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + subpassLoad(uSubpass0, gl_SampleID); +} diff --git a/shaders-reflection/frag/input-attachment.vk.frag b/shaders-reflection/frag/input-attachment.vk.frag new file mode 100644 index 0000000000..f082d15b2a --- /dev/null +++ b/shaders-reflection/frag/input-attachment.vk.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0) + subpassLoad(uSubpass1); +} diff --git a/shaders-reflection/frag/push-constant.vk.frag b/shaders-reflection/frag/push-constant.vk.frag new file mode 100644 index 0000000000..6180faba31 --- /dev/null +++ b/shaders-reflection/frag/push-constant.vk.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor + push.value0 + push.value1; +} diff --git a/shaders-reflection/frag/separate-sampler-texture-array.vk.frag b/shaders-reflection/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000000..b3501c1d8d --- /dev/null +++ b/shaders-reflection/frag/separate-sampler-texture-array.vk.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 1) uniform mediump texture2D uTexture[4]; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D[4]; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube[4]; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray[4]; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture[2], samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +vec4 sample_func_dual_array(mediump sampler samp, mediump texture2D tex[4], vec2 uv) +{ + return texture(sampler2D(tex[1], samp), uv); +} + +void main() +{ + vec2 off = 1.0 / vec2(textureSize(sampler2D(uTexture[1], uSampler), 0)); + vec2 off2 = 1.0 / vec2(textureSize(sampler2D(uTexture[2], uSampler), 1)); + + vec4 c0 = sample_func(uSampler, vTex + off + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture[1], vTex + off + off2); + vec4 c2 = sample_func_dual_array(uSampler, uTexture, vTex + off + off2); + vec4 c3 = texture(sampler2DArray(uTextureArray[3], uSampler), vTex3); + vec4 c4 = texture(samplerCube(uTextureCube[1], uSampler), vTex3); + vec4 c5 = texture(sampler3D(uTexture3D[2], uSampler), vTex3); + + FragColor = c0 + c1 + c2 + c3 + c4 + c5; +} diff --git a/shaders-reflection/frag/spec-constant.vk.frag b/shaders-reflection/frag/spec-constant.vk.frag new file mode 100644 index 0000000000..e62a26059b --- /dev/null +++ b/shaders-reflection/frag/spec-constant.vk.frag @@ -0,0 +1,78 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.5; +layout(constant_id = 2) const float b = 2.5; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; + +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +struct Foo +{ + float elems[d + 2]; +}; + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + // Flexible sized arrays with spec constants and spec constant ops. + float vec0[c + 3][8]; + float vec1[c + 2]; + + Foo foo; + FragColor = vec4(t0 + t1) + vec0[0][0] + vec1[0] + foo.elems[c]; +} diff --git a/shaders-reflection/vert/read-from-row-major-array.vert b/shaders-reflection/vert/read-from-row-major-array.vert new file mode 100644 index 0000000000..792fb8e36c --- /dev/null +++ b/shaders-reflection/vert/read-from-row-major-array.vert @@ -0,0 +1,20 @@ +#version 310 es +layout(location = 0) in highp vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +layout(set = 0, binding = 0, std140, row_major) uniform Block +{ + highp mat2x3 var[3][4]; +}; + +mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; } +mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); } +mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); } + +void main (void) +{ + gl_Position = a_position; + mediump float result = 1.0; + result *= compare_mat2x3(var[0][0], mat2x3(2.0, 6.0, -6.0, 0.0, 5.0, 5.0)); + v_vtxResult = result; +} diff --git a/shaders-reflection/vert/texture_buffer.vert b/shaders-reflection/vert/texture_buffer.vert new file mode 100644 index 0000000000..6bc7ddfae2 --- /dev/null +++ b/shaders-reflection/vert/texture_buffer.vert @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/shaders/asm/frag/switch-label-shared-block.asm.frag b/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000000..8f55bcf536 --- /dev/null +++ b/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,45 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpDecorate %vIndex RelaxedPrecision + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %13 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_8 = OpConstant %float 8 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float_1 = OpConstant %float 1 + %float_3 = OpConstant %float 3 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %int %vIndex + OpSelectionMerge %17 None + OpSwitch %13 %15 0 %14 2 %14 1 %15 8 %17 + %15 = OpLabel + OpBranch %17 + %14 = OpLabel + OpBranch %17 + %17 = OpLabel + %27 = OpPhi %float %float_3 %15 %float_1 %14 %float_8 %5 + OpStore %FragColor %27 + OpReturn + OpFunctionEnd diff --git a/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000000..89036f0eb2 --- /dev/null +++ b/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/shaders/asm/geom/store-uint-layer.invalid.asm.geom b/shaders/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000000..550fc4e990 --- /dev/null +++ b/shaders/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,130 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 74 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %stream_pos %stream_layer %input_pos + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource HLSL 500 + OpName %main "main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "pos" + OpName %GeometryOutput "GeometryOutput" + OpMemberName %GeometryOutput 0 "pos" + OpMemberName %GeometryOutput 1 "layer" + OpName %_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ "@main(struct-VertexOutput-vf41[3];struct-GeometryOutput-vf4-u11;" + OpName %input "input" + OpName %stream "stream" + OpName %output "output" + OpName %v "v" + OpName %stream_pos "stream.pos" + OpName %stream_layer "stream.layer" + OpName %input_0 "input" + OpName %input_pos "input.pos" + OpName %stream_0 "stream" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %stream_pos BuiltIn Position + OpDecorate %stream_layer BuiltIn Layer + OpDecorate %input_pos BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%VertexOutput = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Function__arr_VertexOutput_uint_3 = OpTypePointer Function %_arr_VertexOutput_uint_3 +%GeometryOutput = OpTypeStruct %v4float %uint +%_ptr_Function_GeometryOutput = OpTypePointer Function %GeometryOutput + %15 = OpTypeFunction %void %_ptr_Function__arr_VertexOutput_uint_3 %_ptr_Function_GeometryOutput + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %stream_pos = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_uint = OpTypePointer Output %uint +%stream_layer = OpVariable %_ptr_Output_uint Output +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %input_pos = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_2 = OpConstant %int 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %input_0 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %stream_0 = OpVariable %_ptr_Function_GeometryOutput Function + %param = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %param_0 = OpVariable %_ptr_Function_GeometryOutput Function + %58 = OpAccessChain %_ptr_Input_v4float %input_pos %int_0 + %59 = OpLoad %v4float %58 + %60 = OpAccessChain %_ptr_Function_v4float %input_0 %int_0 %int_0 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_Input_v4float %input_pos %int_1 + %62 = OpLoad %v4float %61 + %63 = OpAccessChain %_ptr_Function_v4float %input_0 %int_1 %int_0 + OpStore %63 %62 + %65 = OpAccessChain %_ptr_Input_v4float %input_pos %int_2 + %66 = OpLoad %v4float %65 + %67 = OpAccessChain %_ptr_Function_v4float %input_0 %int_2 %int_0 + OpStore %67 %66 + %70 = OpLoad %_arr_VertexOutput_uint_3 %input_0 + OpStore %param %70 + %72 = OpFunctionCall %void %_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ %param %param_0 + %73 = OpLoad %GeometryOutput %param_0 + OpStore %stream_0 %73 + OpReturn + OpFunctionEnd +%_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ = OpFunction %void None %15 + %input = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %stream = OpFunctionParameter %_ptr_Function_GeometryOutput + %19 = OpLabel + %output = OpVariable %_ptr_Function_GeometryOutput Function + %v = OpVariable %_ptr_Function_int Function + %25 = OpAccessChain %_ptr_Function_uint %output %int_1 + OpStore %25 %uint_1 + OpStore %v %int_0 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %int %v + %37 = OpSLessThan %bool %34 %int_3 + OpBranchConditional %37 %30 %31 + %30 = OpLabel + %38 = OpLoad %int %v + %40 = OpAccessChain %_ptr_Function_v4float %input %38 %int_0 + %41 = OpLoad %v4float %40 + %42 = OpAccessChain %_ptr_Function_v4float %output %int_0 + OpStore %42 %41 + %45 = OpAccessChain %_ptr_Function_v4float %output %int_0 + %46 = OpLoad %v4float %45 + OpStore %stream_pos %46 + %49 = OpAccessChain %_ptr_Function_uint %output %int_1 + %50 = OpLoad %uint %49 + OpStore %stream_layer %50 + OpEmitVertex + OpBranch %32 + %32 = OpLabel + %51 = OpLoad %int %v + %52 = OpIAdd %int %51 %int_1 + OpStore %v %52 + OpBranch %29 + %31 = OpLabel + OpEndPrimitive + OpReturn + OpFunctionEnd diff --git a/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000000..29b0076a1e --- /dev/null +++ b/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000000..78dccbf044 --- /dev/null +++ b/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/spirv_common.hpp b/spirv_common.hpp index c4716a2388..e3fb2b7238 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -578,6 +578,15 @@ struct SPIRBlock : IVariant MergeSelection }; + enum Hints + { + HintNone, + HintUnroll, + HintDontUnroll, + HintFlatten, + HintDontFlatten + }; + enum Method { MergeToSelectForLoop, @@ -610,6 +619,7 @@ struct SPIRBlock : IVariant Terminator terminator = Unknown; Merge merge = MergeNone; + Hints hint = HintNone; uint32_t next_block = 0; uint32_t merge_block = 0; uint32_t continue_block = 0; diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 0402562e0b..3e460e9f61 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -1063,6 +1063,34 @@ const SPIRType &Compiler::get_type_from_variable(uint32_t id) const return get(get(id).basetype); } +uint32_t Compiler::get_non_pointer_type_id(uint32_t type_id) const +{ + auto *p_type = &get(type_id); + while (p_type->pointer) + { + assert(p_type->parent_type); + type_id = p_type->parent_type; + p_type = &get(type_id); + } + return type_id; +} + +const SPIRType &Compiler::get_non_pointer_type(const SPIRType &type) const +{ + auto *p_type = &type; + while (p_type->pointer) + { + assert(p_type->parent_type); + p_type = &get(p_type->parent_type); + } + return *p_type; +} + +const SPIRType &Compiler::get_non_pointer_type(uint32_t type_id) const +{ + return get_non_pointer_type(get(type_id)); +} + void Compiler::set_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration, const std::string &argument) { @@ -1814,7 +1842,7 @@ void Compiler::parse(const Instruction &instruction) type.basetype = SPIRType::Image; type.image.type = ops[1]; type.image.dim = static_cast(ops[2]); - type.image.depth = ops[3] != 0; + type.image.depth = ops[3] == 1; type.image.arrayed = ops[4] != 0; type.image.ms = ops[5] != 0; type.image.sampled = ops[6]; @@ -2221,6 +2249,14 @@ void Compiler::parse(const Instruction &instruction) current_block->next_block = ops[0]; current_block->merge = SPIRBlock::MergeSelection; selection_merge_targets.insert(current_block->next_block); + + if (length >= 2) + { + if (ops[1] & SelectionControlFlattenMask) + current_block->hint = SPIRBlock::HintFlatten; + else if (ops[1] & SelectionControlDontFlattenMask) + current_block->hint = SPIRBlock::HintDontFlatten; + } break; } @@ -2243,6 +2279,14 @@ void Compiler::parse(const Instruction &instruction) // they are treated as continues. if (current_block->continue_block != current_block->self) continue_blocks.insert(current_block->continue_block); + + if (length >= 3) + { + if (ops[2] & LoopControlUnrollMask) + current_block->hint = SPIRBlock::HintUnroll; + else if (ops[2] & LoopControlDontUnrollMask) + current_block->hint = SPIRBlock::HintDontUnroll; + } break; } @@ -4243,14 +4287,8 @@ bool Compiler::ActiveBuiltinHandler::handle(spv::Op opcode, const uint32_t *args // Required if we access chain into builtins like gl_GlobalInvocationID. add_if_builtin(args[2]); - auto *type = &compiler.get(var->basetype); - // Start traversing type hierarchy at the proper non-pointer types. - while (type->pointer) - { - assert(type->parent_type); - type = &compiler.get(type->parent_type); - } + auto *type = &compiler.get_non_pointer_type(var->basetype); auto &flags = type->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins; @@ -4329,11 +4367,43 @@ bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) void Compiler::analyze_image_and_sampler_usage() { - CombinedImageSamplerUsageHandler handler(*this); + CombinedImageSamplerDrefHandler dref_handler(*this); + traverse_all_reachable_opcodes(get(entry_point), dref_handler); + + CombinedImageSamplerUsageHandler handler(*this, dref_handler.dref_combined_samplers); traverse_all_reachable_opcodes(get(entry_point), handler); - comparison_samplers = move(handler.comparison_samplers); - comparison_images = move(handler.comparison_images); + comparison_ids = move(handler.comparison_ids); need_subpass_input = handler.need_subpass_input; + + // Forward information from separate images and samplers into combined image samplers. + for (auto &combined : combined_image_samplers) + if (comparison_ids.count(combined.sampler_id)) + comparison_ids.insert(combined.combined_id); +} + +bool Compiler::CombinedImageSamplerDrefHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t) +{ + // Mark all sampled images which are used with Dref. + switch (opcode) + { + case OpImageSampleDrefExplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageDrefGather: + case OpImageSparseDrefGather: + dref_combined_samplers.insert(args[2]); + return true; + + default: + break; + } + + return true; } bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length) @@ -4354,20 +4424,12 @@ bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint return true; } -void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_images(uint32_t image) +void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_ids(uint32_t id) { - // Traverse the variable dependency hierarchy and tag everything in its path with comparison images. - comparison_images.insert(image); - for (auto &img : dependency_hierarchy[image]) - add_hierarchy_to_comparison_images(img); -} - -void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_samplers(uint32_t sampler) -{ - // Traverse the variable dependency hierarchy and tag everything in its path with comparison samplers. - comparison_samplers.insert(sampler); - for (auto &samp : dependency_hierarchy[sampler]) - add_hierarchy_to_comparison_samplers(samp); + // Traverse the variable dependency hierarchy and tag everything in its path with comparison ids. + comparison_ids.insert(id); + for (auto &dep_id : dependency_hierarchy[id]) + add_hierarchy_to_comparison_ids(dep_id); } bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_t *args, uint32_t length) @@ -4387,6 +4449,10 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_ auto &type = compiler.get(args[0]); if (type.image.dim == DimSubpassData) need_subpass_input = true; + + // If we load a SampledImage and it will be used with Dref, propagate the state up. + if (dref_combined_samplers.count(args[1]) != 0) + add_hierarchy_to_comparison_ids(args[1]); break; } @@ -4396,16 +4462,20 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_ return false; uint32_t result_type = args[0]; + uint32_t result_id = args[1]; auto &type = compiler.get(result_type); - if (type.image.depth) + if (type.image.depth || dref_combined_samplers.count(result_id) != 0) { // This image must be a depth image. uint32_t image = args[2]; - add_hierarchy_to_comparison_images(image); + add_hierarchy_to_comparison_ids(image); - // This sampler must be a SamplerComparisionState, and not a regular SamplerState. + // This sampler must be a SamplerComparisonState, and not a regular SamplerState. uint32_t sampler = args[3]; - add_hierarchy_to_comparison_samplers(sampler); + add_hierarchy_to_comparison_ids(sampler); + + // Mark the OpSampledImage itself as being comparison state. + comparison_ids.insert(result_id); } return true; } @@ -4567,3 +4637,57 @@ bool Compiler::instruction_to_result_type(uint32_t &result_type, uint32_t &resul return false; } } + +Bitset Compiler::combined_decoration_for_member(const SPIRType &type, uint32_t index) const +{ + Bitset flags; + auto &memb = meta[type.self].members; + if (index >= memb.size()) + return flags; + auto &dec = memb[index]; + + // If our type is a struct, traverse all the members as well recursively. + flags.merge_or(dec.decoration_flags); + for (uint32_t i = 0; i < type.member_types.size(); i++) + flags.merge_or(combined_decoration_for_member(get(type.member_types[i]), i)); + + return flags; +} + +bool Compiler::is_desktop_only_format(spv::ImageFormat format) +{ + switch (format) + { + // Desktop-only formats + case ImageFormatR11fG11fB10f: + case ImageFormatR16f: + case ImageFormatRgb10A2: + case ImageFormatR8: + case ImageFormatRg8: + case ImageFormatR16: + case ImageFormatRg16: + case ImageFormatRgba16: + case ImageFormatR16Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRgba16Snorm: + case ImageFormatR8Snorm: + case ImageFormatRg8Snorm: + case ImageFormatR8ui: + case ImageFormatRg8ui: + case ImageFormatR16ui: + case ImageFormatRgb10a2ui: + case ImageFormatR8i: + case ImageFormatRg8i: + case ImageFormatR16i: + return true; + default: + break; + } + + return false; +} + +bool Compiler::image_is_comparison(const spirv_cross::SPIRType &type, uint32_t id) const +{ + return type.image.depth || (comparison_ids.count(id) != 0); +} diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 12dcae34e9..b25e1c19cb 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -170,6 +170,15 @@ public: // Gets the SPIR-V type of a variable. const SPIRType &get_type_from_variable(uint32_t id) const; + // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. + uint32_t get_non_pointer_type_id(uint32_t type_id) const; + + // Gets the SPIR-V type underlying the given type, which might be a pointer. + const SPIRType &get_non_pointer_type(const SPIRType &type) const; + + // Gets the SPIR-V type underlying the given type_id, which might be a pointer. + const SPIRType &get_non_pointer_type(uint32_t type_id) const; + // Gets the underlying storage class for an OpVariable. spv::StorageClass get_storage_class(uint32_t id) const; @@ -817,8 +826,7 @@ protected: // There might be unrelated IDs found in this set which do not correspond to actual variables. // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs. // Similar is implemented for images, as well as if subpass inputs are needed. - std::unordered_set comparison_samplers; - std::unordered_set comparison_images; + std::unordered_set comparison_ids; bool need_subpass_input = false; // In certain backends, we will need to use a dummy sampler to be able to emit code. @@ -827,23 +835,37 @@ protected: uint32_t dummy_sampler_id = 0; void analyze_image_and_sampler_usage(); + + struct CombinedImageSamplerDrefHandler : OpcodeHandler + { + CombinedImageSamplerDrefHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + std::unordered_set dref_combined_samplers; + }; + struct CombinedImageSamplerUsageHandler : OpcodeHandler { - CombinedImageSamplerUsageHandler(Compiler &compiler_) + CombinedImageSamplerUsageHandler(Compiler &compiler_, + const std::unordered_set &dref_combined_samplers_) : compiler(compiler_) + , dref_combined_samplers(dref_combined_samplers_) { } bool begin_function_scope(const uint32_t *args, uint32_t length) override; bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; Compiler &compiler; + const std::unordered_set &dref_combined_samplers; std::unordered_map> dependency_hierarchy; - std::unordered_set comparison_images; - std::unordered_set comparison_samplers; + std::unordered_set comparison_ids; - void add_hierarchy_to_comparison_samplers(uint32_t sampler); - void add_hierarchy_to_comparison_images(uint32_t sampler); + void add_hierarchy_to_comparison_ids(uint32_t ids); bool need_subpass_input = false; }; @@ -856,6 +878,11 @@ protected: bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, uint32_t length); + Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const; + static bool is_desktop_only_format(spv::ImageFormat format); + + bool image_is_comparison(const SPIRType &type, uint32_t id) const; + private: // Used only to implement the old deprecated get_entry_point() interface. const SPIREntryPoint &get_first_entry_point(const std::string &name) const; diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 500c78b9da..5b71ab1d55 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -689,22 +689,6 @@ void CompilerGLSL::emit_struct(SPIRType &type) statement(""); } -Bitset CompilerGLSL::combined_decoration_for_member(const SPIRType &type, uint32_t index) -{ - Bitset flags; - auto &memb = meta[type.self].members; - if (index >= memb.size()) - return flags; - auto &dec = memb[index]; - - // If our type is a struct, traverse all the members as well recursively. - flags.merge_or(dec.decoration_flags); - for (uint32_t i = 0; i < type.member_types.size(); i++) - flags.merge_or(combined_decoration_for_member(get(type.member_types[i]), i)); - - return flags; -} - string CompilerGLSL::to_interpolation_qualifiers(const Bitset &flags) { string res; @@ -786,10 +770,8 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) { - auto check_desktop = [this] { - if (options.es) - SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile."); - }; + if (options.es && is_desktop_only_format(format)) + SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile."); switch (format) { @@ -807,7 +789,6 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32f"; case ImageFormatRg16f: return "rg16f"; - case ImageFormatRgba32i: return "rgba32i"; case ImageFormatRgba16i: @@ -820,7 +801,6 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32i"; case ImageFormatRg16i: return "rg16i"; - case ImageFormatRgba32ui: return "rgba32ui"; case ImageFormatRgba16ui: @@ -833,71 +813,46 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) return "rg32ui"; case ImageFormatRg16ui: return "rg16ui"; - - // Desktop-only formats case ImageFormatR11fG11fB10f: - check_desktop(); return "r11f_g11f_b10f"; case ImageFormatR16f: - check_desktop(); return "r16f"; case ImageFormatRgb10A2: - check_desktop(); return "rgb10_a2"; case ImageFormatR8: - check_desktop(); return "r8"; case ImageFormatRg8: - check_desktop(); return "rg8"; case ImageFormatR16: - check_desktop(); return "r16"; case ImageFormatRg16: - check_desktop(); return "rg16"; case ImageFormatRgba16: - check_desktop(); return "rgba16"; case ImageFormatR16Snorm: - check_desktop(); return "r16_snorm"; case ImageFormatRg16Snorm: - check_desktop(); return "rg16_snorm"; case ImageFormatRgba16Snorm: - check_desktop(); return "rgba16_snorm"; case ImageFormatR8Snorm: - check_desktop(); return "r8_snorm"; case ImageFormatRg8Snorm: - check_desktop(); return "rg8_snorm"; - case ImageFormatR8ui: - check_desktop(); return "r8ui"; case ImageFormatRg8ui: - check_desktop(); return "rg8ui"; case ImageFormatR16ui: - check_desktop(); return "r16ui"; case ImageFormatRgb10a2ui: - check_desktop(); return "rgb10_a2ui"; - case ImageFormatR8i: - check_desktop(); return "r8i"; case ImageFormatRg8i: - check_desktop(); return "rg8i"; case ImageFormatR16i: - check_desktop(); return "r16i"; - default: case ImageFormatUnknown: return nullptr; @@ -2246,7 +2201,8 @@ void CompilerGLSL::emit_resources() { // For gl_InstanceIndex emulation on GLES, the API user needs to // supply this uniform. - if (meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics) + if (options.vertex.support_nonzero_base_instance && + meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics) { statement("uniform int SPIRV_Cross_BaseInstance;"); emitted = true; @@ -2544,48 +2500,48 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) op = type_to_glsl_constructor(type); break; -#define BOP(opname, x) \ - case Op##opname: \ - binary = true; \ - op = x; \ +#define GLSL_BOP(opname, x) \ + case Op##opname: \ + binary = true; \ + op = x; \ break -#define UOP(opname, x) \ - case Op##opname: \ - unary = true; \ - op = x; \ +#define GLSL_UOP(opname, x) \ + case Op##opname: \ + unary = true; \ + op = x; \ break - UOP(SNegate, "-"); - UOP(Not, "~"); - BOP(IAdd, "+"); - BOP(ISub, "-"); - BOP(IMul, "*"); - BOP(SDiv, "/"); - BOP(UDiv, "/"); - BOP(UMod, "%"); - BOP(SMod, "%"); - BOP(ShiftRightLogical, ">>"); - BOP(ShiftRightArithmetic, ">>"); - BOP(ShiftLeftLogical, "<<"); - BOP(BitwiseOr, "|"); - BOP(BitwiseXor, "^"); - BOP(BitwiseAnd, "&"); - BOP(LogicalOr, "||"); - BOP(LogicalAnd, "&&"); - UOP(LogicalNot, "!"); - BOP(LogicalEqual, "=="); - BOP(LogicalNotEqual, "!="); - BOP(IEqual, "=="); - BOP(INotEqual, "!="); - BOP(ULessThan, "<"); - BOP(SLessThan, "<"); - BOP(ULessThanEqual, "<="); - BOP(SLessThanEqual, "<="); - BOP(UGreaterThan, ">"); - BOP(SGreaterThan, ">"); - BOP(UGreaterThanEqual, ">="); - BOP(SGreaterThanEqual, ">="); + GLSL_UOP(SNegate, "-"); + GLSL_UOP(Not, "~"); + GLSL_BOP(IAdd, "+"); + GLSL_BOP(ISub, "-"); + GLSL_BOP(IMul, "*"); + GLSL_BOP(SDiv, "/"); + GLSL_BOP(UDiv, "/"); + GLSL_BOP(UMod, "%"); + GLSL_BOP(SMod, "%"); + GLSL_BOP(ShiftRightLogical, ">>"); + GLSL_BOP(ShiftRightArithmetic, ">>"); + GLSL_BOP(ShiftLeftLogical, "<<"); + GLSL_BOP(BitwiseOr, "|"); + GLSL_BOP(BitwiseXor, "^"); + GLSL_BOP(BitwiseAnd, "&"); + GLSL_BOP(LogicalOr, "||"); + GLSL_BOP(LogicalAnd, "&&"); + GLSL_UOP(LogicalNot, "!"); + GLSL_BOP(LogicalEqual, "=="); + GLSL_BOP(LogicalNotEqual, "!="); + GLSL_BOP(IEqual, "=="); + GLSL_BOP(INotEqual, "!="); + GLSL_BOP(ULessThan, "<"); + GLSL_BOP(SLessThan, "<"); + GLSL_BOP(ULessThanEqual, "<="); + GLSL_BOP(SLessThanEqual, "<="); + GLSL_BOP(UGreaterThan, ">"); + GLSL_BOP(SGreaterThan, ">"); + GLSL_BOP(UGreaterThanEqual, ">="); + GLSL_BOP(SGreaterThanEqual, ">="); case OpSelect: { @@ -2597,11 +2553,14 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) // In order to preserve its compile-time constness in Vulkan GLSL, // we need to reduce the OpSelect expression back to this simplified model. // If we cannot, fail. - if (!to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0])) + if (to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0])) { - SPIRV_CROSS_THROW( - "Cannot implement specialization constant op OpSelect. " - "Need trivial select implementation which can be resolved to a simple cast from boolean."); + // Implement as a simple cast down below. + } + else + { + // Implement a ternary and pray the compiler understands it :) + return to_ternary_expression(type, cop.arguments[0], cop.arguments[1], cop.arguments[2]); } break; } @@ -2661,8 +2620,8 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) break; } -#undef BOP -#undef UOP +#undef GLSL_BOP +#undef GLSL_UOP if (binary) { if (cop.arguments.size() < 2) @@ -3501,7 +3460,7 @@ bool CompilerGLSL::check_explicit_lod_allowed(uint32_t lod) return allowed; } -string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod) +string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t tex) { const char *type; switch (imgtype.image.dim) @@ -3531,7 +3490,7 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp bool use_explicit_lod = check_explicit_lod_allowed(lod); - if (op == "textureLod" || op == "textureProjLod" || op == "textureGrad") + if (op == "textureLod" || op == "textureProjLod" || op == "textureGrad" || op == "textureProjGrad") { if (is_legacy_es()) { @@ -3542,25 +3501,64 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp require_extension_internal("GL_ARB_shader_texture_lod"); } + if (op == "textureLodOffset" || op == "textureProjLodOffset") + { + if (is_legacy_es()) + SPIRV_CROSS_THROW(join(op, " not allowed in legacy ES")); + + require_extension_internal("GL_EXT_gpu_shader4"); + } + + // GLES has very limited support for shadow samplers. + // Basically shadow2D and shadow2DProj work through EXT_shadow_samplers, + // everything else can just throw + if (image_is_comparison(imgtype, tex) && is_legacy_es()) + { + if (op == "texture" || op == "textureProj") + require_extension_internal("GL_EXT_shadow_samplers"); + else + SPIRV_CROSS_THROW(join(op, " not allowed on depth samplers in legacy ES")); + } + + bool is_es_and_depth = is_legacy_es() && image_is_comparison(imgtype, tex); + std::string type_prefix = image_is_comparison(imgtype, tex) ? "shadow" : "texture"; + if (op == "texture") - return join("texture", type); + return is_es_and_depth ? join(type_prefix, type, "EXT") : join(type_prefix, type); else if (op == "textureLod") { if (use_explicit_lod) - return join("texture", type, is_legacy_es() ? "LodEXT" : "Lod"); + return join(type_prefix, type, is_legacy_es() ? "LodEXT" : "Lod"); else - return join("texture", type); + return join(type_prefix, type); } else if (op == "textureProj") - return join("texture", type, "Proj"); + return join(type_prefix, type, is_es_and_depth ? "ProjEXT" : "Proj"); else if (op == "textureGrad") - return join("texture", type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); + return join(type_prefix, type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); else if (op == "textureProjLod") { if (use_explicit_lod) - return join("texture", type, is_legacy_es() ? "ProjLodEXT" : "ProjLod"); + return join(type_prefix, type, is_legacy_es() ? "ProjLodEXT" : "ProjLod"); else - return join("texture", type); + return join(type_prefix, type, "Proj"); + } + else if (op == "textureLodOffset") + { + if (use_explicit_lod) + return join(type_prefix, type, "LodOffset"); + else + return join(type_prefix, type); + } + else if (op == "textureProjGrad") + return join(type_prefix, type, + is_legacy_es() ? "ProjGradEXT" : is_legacy_desktop() ? "ProjGradARB" : "ProjGrad"); + else if (op == "textureProjLodOffset") + { + if (use_explicit_lod) + return join(type_prefix, type, "ProjLodOffset"); + else + return join(type_prefix, type, "ProjOffset"); } else { @@ -3622,6 +3620,37 @@ bool CompilerGLSL::to_trivial_mix_op(const SPIRType &type, string &op, uint32_t return ret; } +string CompilerGLSL::to_ternary_expression(const SPIRType &restype, uint32_t select, uint32_t true_value, + uint32_t false_value) +{ + string expr; + auto &lerptype = expression_type(select); + + if (lerptype.vecsize == 1) + expr = join(to_enclosed_expression(select), " ? ", to_enclosed_expression(true_value), " : ", + to_enclosed_expression(false_value)); + else + { + auto swiz = [this](uint32_t expression, uint32_t i) { return to_extract_component_expression(expression, i); }; + + expr = type_to_glsl_constructor(restype); + expr += "("; + for (uint32_t i = 0; i < restype.vecsize; i++) + { + expr += swiz(select, i); + expr += " ? "; + expr += swiz(true_value, i); + expr += " : "; + expr += swiz(false_value, i); + if (i + 1 < restype.vecsize) + expr += ", "; + } + expr += ")"; + } + + return expr; +} + void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp) { auto &lerptype = expression_type(lerp); @@ -3652,31 +3681,7 @@ void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, // Could use GL_EXT_shader_integer_mix on desktop at least, // but Apple doesn't support it. :( // Just implement it as ternary expressions. - string expr; - if (lerptype.vecsize == 1) - expr = join(to_enclosed_expression(lerp), " ? ", to_enclosed_expression(right), " : ", - to_enclosed_expression(left)); - else - { - auto swiz = [this](uint32_t expression, uint32_t i) { - return to_extract_component_expression(expression, i); - }; - - expr = type_to_glsl_constructor(restype); - expr += "("; - for (uint32_t i = 0; i < restype.vecsize; i++) - { - expr += swiz(lerp, i); - expr += " ? "; - expr += swiz(right, i); - expr += " : "; - expr += swiz(left, i); - if (i + 1 < restype.vecsize) - expr += ", "; - } - expr += ")"; - } - + auto expr = to_ternary_expression(get(result_type), lerp, right, left); emit_op(result_type, id, expr, should_forward(left) && should_forward(right) && should_forward(lerp)); inherit_expression_dependencies(id, left); inherit_expression_dependencies(id, right); @@ -3759,7 +3764,7 @@ void CompilerGLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_i if (options.vulkan_semantics && combined_image_samplers.empty()) { emit_binary_func_op(result_type, result_id, image_id, samp_id, - type_to_glsl(get(result_type)).c_str()); + type_to_glsl(get(result_type), result_id).c_str()); // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types. forwarded_temporaries.erase(result_id); @@ -3924,6 +3929,10 @@ void CompilerGLSL::emit_texture_op(const Instruction &i) coffset, offset, bias, comp, sample, &forward); expr += ")"; + // texture(samplerXShadow) returns float. shadowX() returns vec4. Swizzle here. + if (is_legacy() && image_is_comparison(imgtype, img)) + expr += ".r"; + emit_op(result_type, id, expr, forward); for (auto &inherit : inherited_expressions) inherit_expression_dependencies(id, inherit); @@ -3944,8 +3953,9 @@ void CompilerGLSL::emit_texture_op(const Instruction &i) // Returns the function name for a texture sampling function for the specified image and sampling characteristics. // For some subclasses, the function is a method on the specified image. -string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj, - bool has_array_offsets, bool has_offset, bool has_grad, bool, uint32_t lod) +string CompilerGLSL::to_function_name(uint32_t tex, const SPIRType &imgtype, bool is_fetch, bool is_gather, + bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad, bool, + uint32_t lod) { string fname; @@ -3955,7 +3965,7 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. bool workaround_lod_array_shadow_as_grad = false; if (((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && - imgtype.image.depth && lod) + image_is_comparison(imgtype, tex) && lod) { auto *constant_lod = maybe_get(lod); if (!constant_lod || constant_lod->scalar_f32() != 0.0f) @@ -3985,7 +3995,7 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is if (has_offset) fname += "Offset"; - return is_legacy() ? legacy_tex_op(fname, imgtype, lod) : fname; + return is_legacy() ? legacy_tex_op(fname, imgtype, lod, tex) : fname; } std::string CompilerGLSL::convert_separate_image_to_combined(uint32_t id) @@ -4070,7 +4080,7 @@ string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &imgtype, boo // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. bool workaround_lod_array_shadow_as_grad = ((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && - imgtype.image.depth && lod; + image_is_comparison(imgtype, img) && lod; if (dref) { @@ -4484,6 +4494,16 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtOffset"); break; + case GLSLstd450NMin: + emit_binary_func_op(result_type, id, args[0], args[1], "unsupported_glsl450_nmin"); + break; + case GLSLstd450NMax: + emit_binary_func_op(result_type, id, args[0], args[1], "unsupported_glsl450_nmax"); + break; + case GLSLstd450NClamp: + emit_binary_func_op(result_type, id, args[0], args[1], "unsupported_glsl450_nclamp"); + break; + default: statement("// unimplemented GLSL op ", eop); break; @@ -4802,7 +4822,7 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i) break; // clang-format off -#define GROUP_OP(op, glsl_op) \ +#define GLSL_GROUP_OP(op, glsl_op) \ case OpGroupNonUniform##op: \ { \ auto operation = static_cast(ops[3]); \ @@ -4818,20 +4838,20 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } - GROUP_OP(FAdd, Add) - GROUP_OP(FMul, Mul) - GROUP_OP(FMin, Min) - GROUP_OP(FMax, Max) - GROUP_OP(IAdd, Add) - GROUP_OP(IMul, Mul) - GROUP_OP(SMin, Min) - GROUP_OP(SMax, Max) - GROUP_OP(UMin, Min) - GROUP_OP(UMax, Max) - GROUP_OP(BitwiseAnd, And) - GROUP_OP(BitwiseOr, Or) - GROUP_OP(BitwiseXor, Xor) -#undef GROUP_OP + GLSL_GROUP_OP(FAdd, Add) + GLSL_GROUP_OP(FMul, Mul) + GLSL_GROUP_OP(FMin, Min) + GLSL_GROUP_OP(FMax, Max) + GLSL_GROUP_OP(IAdd, Add) + GLSL_GROUP_OP(IMul, Mul) + GLSL_GROUP_OP(SMin, Min) + GLSL_GROUP_OP(SMax, Max) + GLSL_GROUP_OP(UMin, Min) + GLSL_GROUP_OP(UMax, Max) + GLSL_GROUP_OP(BitwiseAnd, And) + GLSL_GROUP_OP(BitwiseOr, Or) + GLSL_GROUP_OP(BitwiseXor, Xor) +#undef GLSL_GROUP_OP // clang-format on case OpGroupNonUniformQuadSwap: @@ -4961,10 +4981,15 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) case BuiltInInstanceIndex: if (options.vulkan_semantics) return "gl_InstanceIndex"; - else + else if (options.vertex.support_nonzero_base_instance) return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID. + else + return "gl_InstanceID"; case BuiltInPrimitiveId: - return "gl_PrimitiveID"; + if (storage == StorageClassInput && get_entry_point().model == ExecutionModelGeometry) + return "gl_PrimitiveIDIn"; + else + return "gl_PrimitiveID"; case BuiltInInvocationId: return "gl_InvocationID"; case BuiltInLayer: @@ -5119,16 +5144,10 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice if (!chain_only) expr = to_enclosed_expression(base); - uint32_t type_id = expression_type_id(base); - const auto *type = &get(type_id); - // Start traversing type hierarchy at the proper non-pointer types, // but keep type_id referencing the original pointer for use below. - while (type->pointer) - { - assert(type->parent_type); - type = &get(type->parent_type); - } + uint32_t type_id = expression_type_id(base); + const auto *type = &get_non_pointer_type(type_id); bool access_chain_is_arrayed = expr.find_first_of('[') != string::npos; bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base); @@ -5571,14 +5590,8 @@ std::pair CompilerGLSL::flattened_access_chain_offset(con bool *need_transpose, uint32_t *out_matrix_stride) { - const auto *type = &basetype; - // Start traversing type hierarchy at the proper non-pointer types. - while (type->pointer) - { - assert(type->parent_type); - type = &get(type->parent_type); - } + const auto *type = &get_non_pointer_type(basetype); // This holds the type of the current pointer which we are traversing through. // We always start out from a struct type which is the block. @@ -6081,17 +6094,17 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) auto opcode = static_cast(instruction.op); uint32_t length = instruction.length; -#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BOP_CAST(op, type) \ +#define GLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define GLSL_BOP_CAST(op, type) \ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, glsl_opcode_is_sign_invariant(opcode)) -#define UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) -#define QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) -#define TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BFOP_CAST(op, type) \ +#define GLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define GLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define GLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define GLSL_BFOP_CAST(op, type) \ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, glsl_opcode_is_sign_invariant(opcode)) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define GLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) switch (opcode) { @@ -6125,6 +6138,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) auto expr = to_expression(ptr); + // We might need to bitcast in order to load from a builtin. + bitcast_from_builtin_load(ptr, expr, get(result_type)); + if (ptr_expression) ptr_expression->need_transpose = old_need_transpose; @@ -6182,11 +6198,15 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) else { auto rhs = to_expression(ops[1]); + // Statements to OpStore may be empty if it is a struct with zero members. Just forward the store to /dev/null. if (!rhs.empty()) { auto lhs = to_expression(ops[0]); + // We might need to bitcast in order to store to a builtin. + bitcast_to_builtin_store(ops[0], rhs, expression_type(ops[1])); + // Tries to optimize assignments like " = op expr". // While this is purely cosmetic, this is important for legacy ESSL where loop // variable increments must be in either i++ or i += const-expr. @@ -6617,45 +6637,45 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // ALU case OpIsNan: - UFOP(isnan); + GLSL_UFOP(isnan); break; case OpIsInf: - UFOP(isinf); + GLSL_UFOP(isinf); break; case OpSNegate: case OpFNegate: - UOP(-); + GLSL_UOP(-); break; case OpIAdd: { // For simple arith ops, prefer the output type if there's a mismatch to avoid extra bitcasts. auto type = get(ops[0]).basetype; - BOP_CAST(+, type); + GLSL_BOP_CAST(+, type); break; } case OpFAdd: - BOP(+); + GLSL_BOP(+); break; case OpISub: { auto type = get(ops[0]).basetype; - BOP_CAST(-, type); + GLSL_BOP_CAST(-, type); break; } case OpFSub: - BOP(-); + GLSL_BOP(-); break; case OpIMul: { auto type = get(ops[0]).basetype; - BOP_CAST(*, type); + GLSL_BOP_CAST(*, type); break; } @@ -6671,7 +6691,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) e->need_transpose = true; } else - BOP(*); + GLSL_BOP(*); break; } @@ -6679,19 +6699,19 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpMatrixTimesScalar: case OpVectorTimesScalar: case OpMatrixTimesMatrix: - BOP(*); + GLSL_BOP(*); break; case OpOuterProduct: - BFOP(outerProduct); + GLSL_BFOP(outerProduct); break; case OpDot: - BFOP(dot); + GLSL_BFOP(dot); break; case OpTranspose: - UFOP(transpose); + GLSL_UFOP(transpose); break; case OpSRem: @@ -6713,67 +6733,67 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) } case OpSDiv: - BOP_CAST(/, SPIRType::Int); + GLSL_BOP_CAST(/, SPIRType::Int); break; case OpUDiv: - BOP_CAST(/, SPIRType::UInt); + GLSL_BOP_CAST(/, SPIRType::UInt); break; case OpFDiv: - BOP(/); + GLSL_BOP(/); break; case OpShiftRightLogical: - BOP_CAST(>>, SPIRType::UInt); + GLSL_BOP_CAST(>>, SPIRType::UInt); break; case OpShiftRightArithmetic: - BOP_CAST(>>, SPIRType::Int); + GLSL_BOP_CAST(>>, SPIRType::Int); break; case OpShiftLeftLogical: { auto type = get(ops[0]).basetype; - BOP_CAST(<<, type); + GLSL_BOP_CAST(<<, type); break; } case OpBitwiseOr: { auto type = get(ops[0]).basetype; - BOP_CAST(|, type); + GLSL_BOP_CAST(|, type); break; } case OpBitwiseXor: { auto type = get(ops[0]).basetype; - BOP_CAST (^, type); + GLSL_BOP_CAST (^, type); break; } case OpBitwiseAnd: { auto type = get(ops[0]).basetype; - BOP_CAST(&, type); + GLSL_BOP_CAST(&, type); break; } case OpNot: - UOP(~); + GLSL_UOP(~); break; case OpUMod: - BOP_CAST(%, SPIRType::UInt); + GLSL_BOP_CAST(%, SPIRType::UInt); break; case OpSMod: - BOP_CAST(%, SPIRType::Int); + GLSL_BOP_CAST(%, SPIRType::Int); break; case OpFMod: - BFOP(mod); + GLSL_BFOP(mod); break; case OpFRem: @@ -6800,11 +6820,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // Relational case OpAny: - UFOP(any); + GLSL_UFOP(any); break; case OpAll: - UFOP(all); + GLSL_UFOP(all); break; case OpSelect: @@ -6821,7 +6841,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (type.vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||"); else - BOP(||); + GLSL_BOP(||); break; } @@ -6835,7 +6855,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (type.vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&"); else - BOP(&&); + GLSL_BOP(&&); break; } @@ -6843,18 +6863,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto &type = get(ops[0]); if (type.vecsize > 1) - UFOP(not); + GLSL_UFOP(not); else - UOP(!); + GLSL_UOP(!); break; } case OpIEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(equal, SPIRType::Int); + GLSL_BFOP_CAST(equal, SPIRType::Int); else - BOP_CAST(==, SPIRType::Int); + GLSL_BOP_CAST(==, SPIRType::Int); break; } @@ -6862,18 +6882,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpFOrdEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(equal); + GLSL_BFOP(equal); else - BOP(==); + GLSL_BOP(==); break; } case OpINotEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(notEqual, SPIRType::Int); + GLSL_BFOP_CAST(notEqual, SPIRType::Int); else - BOP_CAST(!=, SPIRType::Int); + GLSL_BOP_CAST(!=, SPIRType::Int); break; } @@ -6881,9 +6901,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpFOrdNotEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(notEqual); + GLSL_BFOP(notEqual); else - BOP(!=); + GLSL_BOP(!=); break; } @@ -6892,18 +6912,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpUGreaterThan ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(greaterThan, type); + GLSL_BFOP_CAST(greaterThan, type); else - BOP_CAST(>, type); + GLSL_BOP_CAST(>, type); break; } case OpFOrdGreaterThan: { if (expression_type(ops[2]).vecsize > 1) - BFOP(greaterThan); + GLSL_BFOP(greaterThan); else - BOP(>); + GLSL_BOP(>); break; } @@ -6912,18 +6932,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpUGreaterThanEqual ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(greaterThanEqual, type); + GLSL_BFOP_CAST(greaterThanEqual, type); else - BOP_CAST(>=, type); + GLSL_BOP_CAST(>=, type); break; } case OpFOrdGreaterThanEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(greaterThanEqual); + GLSL_BFOP(greaterThanEqual); else - BOP(>=); + GLSL_BOP(>=); break; } @@ -6932,18 +6952,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpULessThan ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(lessThan, type); + GLSL_BFOP_CAST(lessThan, type); else - BOP_CAST(<, type); + GLSL_BOP_CAST(<, type); break; } case OpFOrdLessThan: { if (expression_type(ops[2]).vecsize > 1) - BFOP(lessThan); + GLSL_BFOP(lessThan); else - BOP(<); + GLSL_BOP(<); break; } @@ -6952,18 +6972,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { auto type = opcode == OpULessThanEqual ? SPIRType::UInt : SPIRType::Int; if (expression_type(ops[2]).vecsize > 1) - BFOP_CAST(lessThanEqual, type); + GLSL_BFOP_CAST(lessThanEqual, type); else - BOP_CAST(<=, type); + GLSL_BOP_CAST(<=, type); break; } case OpFOrdLessThanEqual: { if (expression_type(ops[2]).vecsize > 1) - BFOP(lessThanEqual); + GLSL_BFOP(lessThanEqual); else - BOP(<=); + GLSL_BOP(<=); break; } @@ -7037,21 +7057,21 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // Derivatives case OpDPdx: - UFOP(dFdx); + GLSL_UFOP(dFdx); if (is_legacy_es()) require_extension_internal("GL_OES_standard_derivatives"); register_control_dependent_expression(ops[1]); break; case OpDPdy: - UFOP(dFdy); + GLSL_UFOP(dFdy); if (is_legacy_es()) require_extension_internal("GL_OES_standard_derivatives"); register_control_dependent_expression(ops[1]); break; case OpDPdxFine: - UFOP(dFdxFine); + GLSL_UFOP(dFdxFine); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7062,7 +7082,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpDPdyFine: - UFOP(dFdyFine); + GLSL_UFOP(dFdyFine); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7077,14 +7097,14 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); } - UFOP(dFdxCoarse); + GLSL_UFOP(dFdxCoarse); if (options.version < 450) require_extension_internal("GL_ARB_derivative_control"); register_control_dependent_expression(ops[1]); break; case OpDPdyCoarse: - UFOP(dFdyCoarse); + GLSL_UFOP(dFdyCoarse); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7095,14 +7115,14 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpFwidth: - UFOP(fwidth); + GLSL_UFOP(fwidth); if (is_legacy_es()) require_extension_internal("GL_OES_standard_derivatives"); register_control_dependent_expression(ops[1]); break; case OpFwidthCoarse: - UFOP(fwidthCoarse); + GLSL_UFOP(fwidthCoarse); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7113,7 +7133,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpFwidthFine: - UFOP(fwidthFine); + GLSL_UFOP(fwidthFine); if (options.es) { SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); @@ -7126,21 +7146,21 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) // Bitfield case OpBitFieldInsert: // TODO: The signedness of inputs is strict in GLSL, but not in SPIR-V, bitcast if necessary. - QFOP(bitfieldInsert); + GLSL_QFOP(bitfieldInsert); break; case OpBitFieldSExtract: case OpBitFieldUExtract: // TODO: The signedness of inputs is strict in GLSL, but not in SPIR-V, bitcast if necessary. - TFOP(bitfieldExtract); + GLSL_TFOP(bitfieldExtract); break; case OpBitReverse: - UFOP(bitfieldReverse); + GLSL_UFOP(bitfieldReverse); break; case OpBitCount: - UFOP(bitCount); + GLSL_UFOP(bitCount); break; // Atomics @@ -7177,7 +7197,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) flush_all_atomic_capable_variables(); // FIXME: Image? // OpAtomicLoad seems to only be relevant for atomic counters. - UFOP(atomicCounter); + GLSL_UFOP(atomicCounter); register_read(ops[1], ops[2], should_forward(ops[2])); break; @@ -7187,7 +7207,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpAtomicIIncrement: forced_temporaries.insert(ops[1]); // FIXME: Image? - UFOP(atomicCounterIncrement); + GLSL_UFOP(atomicCounterIncrement); flush_all_atomic_capable_variables(); register_read(ops[1], ops[2], should_forward(ops[2])); break; @@ -7195,7 +7215,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpAtomicIDecrement: forced_temporaries.insert(ops[1]); // FIXME: Image? - UFOP(atomicCounterDecrement); + GLSL_UFOP(atomicCounterDecrement); flush_all_atomic_capable_variables(); register_read(ops[1], ops[2], should_forward(ops[2])); break; @@ -7326,12 +7346,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { require_extension_internal("GL_ARB_texture_query_lod"); // For some reason, the ARB spec is all-caps. - BFOP(textureQueryLOD); + GLSL_BFOP(textureQueryLOD); } else if (options.es) SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile."); else - BFOP(textureQueryLod); + GLSL_BFOP(textureQueryLod); register_control_dependent_expression(ops[1]); break; } @@ -7987,6 +8007,30 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) emit_subgroup_op(instruction); break; + case OpFUnordEqual: + GLSL_BFOP(unsupported_FUnordEqual); + break; + + case OpFUnordNotEqual: + GLSL_BFOP(unsupported_FUnordNotEqual); + break; + + case OpFUnordLessThan: + GLSL_BFOP(unsupported_FUnordLessThan); + break; + + case OpFUnordGreaterThan: + GLSL_BFOP(unsupported_FUnordGreaterThan); + break; + + case OpFUnordLessThanEqual: + GLSL_BFOP(unsupported_FUnordLessThanEqual); + break; + + case OpFUnordGreaterThanEqual: + GLSL_BFOP(unsupported_FUnordGreaterThanEqual); + break; + default: statement("// unimplemented op ", instruction.op); break; @@ -8375,7 +8419,7 @@ string CompilerGLSL::type_to_array_glsl(const SPIRType &type) } } -string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t /* id */) +string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id) { auto &imagetype = get(type.image.type); string res; @@ -8448,8 +8492,11 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t /* id */) } // "Shadow" state in GLSL only exists for samplers and combined image samplers. - if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && type.image.depth) + if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && + image_is_comparison(type, id)) + { res += "Shadow"; + } return res; } @@ -8495,7 +8542,7 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) case SPIRType::Sampler: // The depth field is set by calling code based on the variable ID of the sampler, effectively reintroducing // this distinction into the type system. - return comparison_samplers.count(id) ? "samplerShadow" : "sampler"; + return comparison_ids.count(id) ? "samplerShadow" : "sampler"; case SPIRType::Void: return "void"; @@ -8713,13 +8760,8 @@ void CompilerGLSL::add_function_overload(const SPIRFunction &func) // Parameters can vary with pointer type or not, // but that will not change the signature in GLSL/HLSL, // so strip the pointer type before hashing. - uint32_t type_id = arg.type; - auto *type = &get(type_id); - while (type->pointer) - { - type_id = type->parent_type; - type = &get(type_id); - } + uint32_t type_id = get_non_pointer_type_id(arg.type); + auto &type = get(type_id); if (!combined_image_samplers.empty()) { @@ -8727,8 +8769,8 @@ void CompilerGLSL::add_function_overload(const SPIRFunction &func) // we pass down to callees, because they may be shuffled around. // Ignore these arguments, to make sure that functions need to differ in some other way // to be considered different overloads. - if (type->basetype == SPIRType::SampledImage || - (type->basetype == SPIRType::Image && type->image.sampled == 1) || type->basetype == SPIRType::Sampler) + if (type.basetype == SPIRType::SampledImage || + (type.basetype == SPIRType::Image && type.image.sampled == 1) || type.basetype == SPIRType::Sampler) { continue; } @@ -9123,6 +9165,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uin if (true_sub) { + emit_block_hints(get(from)); statement("if (", to_expression(cond), ")"); begin_scope(); branch(from, true_block); @@ -9146,6 +9189,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uin else if (false_sub && !true_sub) { // Only need false path, use negative conditional. + emit_block_hints(get(from)); statement("if (!", to_enclosed_expression(cond), ")"); begin_scope(); branch(from, false_block); @@ -9374,6 +9418,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method { // This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header. flush_undeclared_variables(block); + emit_block_hints(block); // Important that we do this in this order because // emitting the continue block can invalidate the condition expression. @@ -9392,6 +9437,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method case SPIRBlock::WhileLoop: // This block may be a dominating block, so make sure we flush undeclared variables before building the while loop header. flush_undeclared_variables(block); + emit_block_hints(block); statement("while (", to_expression(block.condition), ")"); break; @@ -9439,11 +9485,13 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method auto initializer = emit_for_loop_initializers(block); auto condition = to_expression(child.condition); auto continue_block = emit_continue_block(block.continue_block); + emit_block_hints(block); statement("for (", initializer, "; ", condition, "; ", continue_block, ")"); break; } case SPIRBlock::WhileLoop: + emit_block_hints(block); statement("while (", to_expression(child.condition), ")"); break; @@ -9654,35 +9702,65 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) auto &type = expression_type(block.condition); bool uint32_t_case = type.basetype == SPIRType::UInt; + emit_block_hints(block); statement("switch (", to_expression(block.condition), ")"); begin_scope(); + // Multiple case labels can branch to same block, so find all unique blocks. + bool emitted_default = false; + unordered_set emitted_blocks; + for (auto &c : block.cases) { - auto case_value = - uint32_t_case ? convert_to_string(uint32_t(c.value)) : convert_to_string(int32_t(c.value)); - statement("case ", case_value, ":"); + if (emitted_blocks.count(c.block) != 0) + continue; + + // Emit all case labels which branch to our target. + // FIXME: O(n^2), revisit if we hit shaders with 100++ case labels ... + for (auto &other_case : block.cases) + { + if (other_case.block == c.block) + { + auto case_value = uint32_t_case ? convert_to_string(uint32_t(other_case.value)) : + convert_to_string(int32_t(other_case.value)); + statement("case ", case_value, ":"); + } + } + + // Maybe we share with default block? + if (block.default_block == c.block) + { + statement("default:"); + emitted_default = true; + } + + // Complete the target. + emitted_blocks.insert(c.block); + begin_scope(); branch(block.self, c.block); end_scope(); } - if (block.default_block != block.next_block) + if (!emitted_default) { - statement("default:"); - begin_scope(); - if (is_break(block.default_block)) - SPIRV_CROSS_THROW("Cannot break; out of a switch statement and out of a loop at the same time ..."); - branch(block.self, block.default_block); - end_scope(); - } - else if (flush_phi_required(block.self, block.next_block)) - { - statement("default:"); - begin_scope(); - flush_phi(block.self, block.next_block); - statement("break;"); - end_scope(); + if (block.default_block != block.next_block) + { + statement("default:"); + begin_scope(); + if (is_break(block.default_block)) + SPIRV_CROSS_THROW("Cannot break; out of a switch statement and out of a loop at the same time ..."); + branch(block.self, block.default_block); + end_scope(); + } + else if (flush_phi_required(block.self, block.next_block)) + { + statement("default:"); + begin_scope(); + flush_phi(block.self, block.next_block); + statement("break;"); + end_scope(); + } } end_scope(); @@ -9874,3 +9952,70 @@ void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id) { statement(lhs, " = ", to_expression(rhs_id), ";"); } + +void CompilerGLSL::bitcast_from_builtin_load(uint32_t source_id, std::string &expr, + const spirv_cross::SPIRType &expr_type) +{ + // Only interested in standalone builtin variables. + if (!has_decoration(source_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(source_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInSampleId: + expected_type = SPIRType::Int; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + expr = bitcast_expression(expr_type, expected_type, expr); +} + +void CompilerGLSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr, + const spirv_cross::SPIRType &expr_type) +{ + // Only interested in standalone builtin variables. + if (!has_decoration(target_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(target_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + expected_type = SPIRType::Int; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + { + auto type = expr_type; + type.basetype = expected_type; + expr = bitcast_expression(type, expr_type.basetype, expr); + } +} + +void CompilerGLSL::emit_block_hints(const SPIRBlock &) +{ +} diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index e02ef34802..7205def759 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -105,6 +105,11 @@ public: // Inverts gl_Position.y or equivalent. bool flip_vert_y = false; + + // If true, the backend will assume that InstanceIndex will need to apply + // a base instance offset. Set to false if you know you will never use base instance + // functionality as it might remove some internal uniforms. + bool support_nonzero_base_instance = true; } vertex; struct @@ -276,7 +281,6 @@ protected: { for (uint32_t i = 0; i < indent; i++) (*buffer) << " "; - statement_inner(std::forward(ts)...); (*buffer) << '\n'; } @@ -404,6 +408,9 @@ protected: SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type, uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type); + std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value, + uint32_t false_value); + void emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); bool expression_is_forwarded(uint32_t id); SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs, @@ -451,11 +458,11 @@ protected: const char *format_to_glsl(spv::ImageFormat format); virtual std::string layout_for_member(const SPIRType &type, uint32_t index); virtual std::string to_interpolation_qualifiers(const Bitset &flags); - Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index); std::string layout_for_variable(const SPIRVariable &variable); std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id); virtual bool skip_argument(uint32_t id) const; virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id); + virtual void emit_block_hints(const SPIRBlock &block); bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0, uint32_t end_offset = std::numeric_limits::max()); @@ -484,7 +491,7 @@ protected: void replace_fragment_output(SPIRVariable &var); void replace_fragment_outputs(); bool check_explicit_lod_allowed(uint32_t lod); - std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod); + std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t id); uint32_t indent = 0; @@ -562,6 +569,12 @@ protected: std::string convert_separate_image_to_combined(uint32_t id); + // Builtins in GLSL are always specific signedness, but the SPIR-V can declare them + // as either unsigned or signed. + // Sometimes we will need to automatically perform bitcasts on load and store to make this work. + virtual void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); + virtual void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); + private: void init() { diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index fffdf3129a..aaa05cc07e 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -224,7 +224,7 @@ static bool hlsl_opcode_is_sign_invariant(Op opcode) } } -string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type) +string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t) { auto &imagetype = get(type.image.type); const char *dim = nullptr; @@ -275,7 +275,7 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type) ">"); } -string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type) +string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t id) { auto &imagetype = get(type.image.type); string res; @@ -338,18 +338,18 @@ string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type) res += "MS"; if (type.image.arrayed) res += "Array"; - if (type.image.depth) + if (image_is_comparison(type, id)) res += "Shadow"; return res; } -string CompilerHLSL::image_type_hlsl(const SPIRType &type) +string CompilerHLSL::image_type_hlsl(const SPIRType &type, uint32_t id) { if (hlsl_options.shader_model <= 30) - return image_type_hlsl_legacy(type); + return image_type_hlsl_legacy(type, id); else - return image_type_hlsl_modern(type); + return image_type_hlsl_modern(type, id); } // The optional id parameter indicates the object whose type we are trying @@ -370,10 +370,10 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id) case SPIRType::Image: case SPIRType::SampledImage: - return image_type_hlsl(type); + return image_type_hlsl(type, id); case SPIRType::Sampler: - return comparison_samplers.count(id) ? "SamplerComparisonState" : "SamplerState"; + return comparison_ids.count(id) ? "SamplerComparisonState" : "SamplerState"; case SPIRType::Void: return "void"; @@ -1838,9 +1838,10 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var) { Bitset flags = get_buffer_block_flags(var); bool is_readonly = flags.get(DecorationNonWritable); + bool is_coherent = flags.get(DecorationCoherent); add_resource_name(var.self); - statement(is_readonly ? "ByteAddressBuffer " : "RWByteAddressBuffer ", to_name(var.self), - type_to_array_glsl(type), to_resource_binding(var), ";"); + statement(is_coherent ? "globallycoherent " : "", is_readonly ? "ByteAddressBuffer " : "RWByteAddressBuffer ", + to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); } else { @@ -2059,7 +2060,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret { // Manufacture automatic sampler arg for SampledImage texture decl += ", "; - decl += join(arg_type.image.depth ? "SamplerComparisonState " : "SamplerState ", + decl += join(image_is_comparison(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ", to_sampler_expression(arg.id), type_to_array_glsl(arg_type)); } @@ -2574,7 +2575,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i) { texop += img_expr; - if (imgtype.image.depth) + if (image_is_comparison(imgtype, img)) { if (gather) { @@ -2922,13 +2923,17 @@ void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) case SPIRType::SampledImage: case SPIRType::Image: { - statement(image_type_hlsl_modern(type), " ", to_name(var.self), type_to_array_glsl(type), - to_resource_binding(var), ";"); + bool is_coherent = false; + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + is_coherent = has_decoration(var.self, DecorationCoherent); + + statement(is_coherent ? "globallycoherent " : "", image_type_hlsl_modern(type, var.self), " ", + to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer) { // For combined image samplers, also emit a combined image sampler. - if (type.image.depth) + if (image_is_comparison(type, var.self)) statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type), to_resource_binding_sampler(var), ";"); else @@ -2939,7 +2944,7 @@ void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) } case SPIRType::Sampler: - if (comparison_samplers.count(var.self)) + if (comparison_ids.count(var.self)) statement("SamplerComparisonState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); else @@ -3524,14 +3529,8 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction) else base = to_expression(ops[2]); - auto *basetype = &type; - // Start traversing type hierarchy at the proper non-pointer types. - while (basetype->pointer) - { - assert(basetype->parent_type); - basetype = &get(basetype->parent_type); - } + auto *basetype = &get_non_pointer_type(type); // Traverse the type hierarchy down to the actual buffer types. for (uint32_t i = 0; i < to_plain_buffer_length; i++) @@ -3767,7 +3766,7 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i) } // clang-format off -#define GROUP_OP(op, hlsl_op, supports_scan) \ +#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \ case OpGroupNonUniform##op: \ { \ auto operation = static_cast(ops[3]); \ @@ -3787,20 +3786,20 @@ case OpGroupNonUniform##op: \ SPIRV_CROSS_THROW("Invalid group operation."); \ break; \ } - GROUP_OP(FAdd, Sum, true) - GROUP_OP(FMul, Product, true) - GROUP_OP(FMin, Min, false) - GROUP_OP(FMax, Max, false) - GROUP_OP(IAdd, Sum, true) - GROUP_OP(IMul, Product, true) - GROUP_OP(SMin, Min, false) - GROUP_OP(SMax, Max, false) - GROUP_OP(UMin, Min, false) - GROUP_OP(UMax, Max, false) - GROUP_OP(BitwiseAnd, BitAnd, false) - GROUP_OP(BitwiseOr, BitOr, false) - GROUP_OP(BitwiseXor, BitXor, false) -#undef GROUP_OP + HLSL_GROUP_OP(FAdd, Sum, true) + HLSL_GROUP_OP(FMul, Product, true) + HLSL_GROUP_OP(FMin, Min, false) + HLSL_GROUP_OP(FMax, Max, false) + HLSL_GROUP_OP(IAdd, Sum, true) + HLSL_GROUP_OP(IMul, Product, true) + HLSL_GROUP_OP(SMin, Min, false) + HLSL_GROUP_OP(SMax, Max, false) + HLSL_GROUP_OP(UMin, Min, false) + HLSL_GROUP_OP(UMax, Max, false) + HLSL_GROUP_OP(BitwiseAnd, BitAnd, false) + HLSL_GROUP_OP(BitwiseOr, BitOr, false) + HLSL_GROUP_OP(BitwiseXor, BitXor, false) +#undef HLSL_GROUP_OP // clang-format on case OpGroupNonUniformQuadSwap: @@ -3835,17 +3834,17 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) auto ops = stream(instruction); auto opcode = static_cast(instruction.op); -#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BOP_CAST(op, type) \ +#define HLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_BOP_CAST(op, type) \ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, hlsl_opcode_is_sign_invariant(opcode)) -#define UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) -#define QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) -#define TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BFOP_CAST(op, type) \ +#define HLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define HLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define HLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_BFOP_CAST(op, type) \ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, hlsl_opcode_is_sign_invariant(opcode)) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) switch (opcode) { @@ -3914,39 +3913,39 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) } case OpDPdx: - UFOP(ddx); + HLSL_UFOP(ddx); register_control_dependent_expression(ops[1]); break; case OpDPdy: - UFOP(ddy); + HLSL_UFOP(ddy); register_control_dependent_expression(ops[1]); break; case OpDPdxFine: - UFOP(ddx_fine); + HLSL_UFOP(ddx_fine); register_control_dependent_expression(ops[1]); break; case OpDPdyFine: - UFOP(ddy_fine); + HLSL_UFOP(ddy_fine); register_control_dependent_expression(ops[1]); break; case OpDPdxCoarse: - UFOP(ddx_coarse); + HLSL_UFOP(ddx_coarse); register_control_dependent_expression(ops[1]); break; case OpDPdyCoarse: - UFOP(ddy_coarse); + HLSL_UFOP(ddy_coarse); register_control_dependent_expression(ops[1]); break; case OpFwidth: case OpFwidthCoarse: case OpFwidthFine: - UFOP(fwidth); + HLSL_UFOP(fwidth); register_control_dependent_expression(ops[1]); break; @@ -3959,7 +3958,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (type.vecsize > 1) emit_unrolled_unary_op(result_type, id, ops[2], "!"); else - UOP(!); + HLSL_UOP(!); break; } @@ -3971,7 +3970,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "=="); else - BOP_CAST(==, SPIRType::Int); + HLSL_BOP_CAST(==, SPIRType::Int); break; } @@ -3984,7 +3983,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "=="); else - BOP(==); + HLSL_BOP(==); break; } @@ -3996,7 +3995,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!="); else - BOP_CAST(!=, SPIRType::Int); + HLSL_BOP_CAST(!=, SPIRType::Int); break; } @@ -4009,7 +4008,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!="); else - BOP(!=); + HLSL_BOP(!=); break; } @@ -4023,7 +4022,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">"); else - BOP_CAST(>, type); + HLSL_BOP_CAST(>, type); break; } @@ -4035,7 +4034,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">"); else - BOP(>); + HLSL_BOP(>); break; } @@ -4049,7 +4048,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">="); else - BOP_CAST(>=, type); + HLSL_BOP_CAST(>=, type); break; } @@ -4061,7 +4060,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">="); else - BOP(>=); + HLSL_BOP(>=); break; } @@ -4075,7 +4074,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<"); else - BOP_CAST(<, type); + HLSL_BOP_CAST(<, type); break; } @@ -4087,7 +4086,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<"); else - BOP(<); + HLSL_BOP(<); break; } @@ -4101,7 +4100,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<="); else - BOP_CAST(<=, type); + HLSL_BOP_CAST(<=, type); break; } @@ -4113,7 +4112,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (expression_type(ops[2]).vecsize > 1) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<="); else - BOP(<=); + HLSL_BOP(<=); break; } @@ -4418,18 +4417,18 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) } if (opcode == OpBitFieldSExtract) - TFOP(SPIRV_Cross_bitfieldSExtract); + HLSL_TFOP(SPIRV_Cross_bitfieldSExtract); else - TFOP(SPIRV_Cross_bitfieldUExtract); + HLSL_TFOP(SPIRV_Cross_bitfieldUExtract); break; } case OpBitCount: - UFOP(countbits); + HLSL_UFOP(countbits); break; case OpBitReverse: - UFOP(reversebits); + HLSL_UFOP(reversebits); break; default: @@ -4605,3 +4604,24 @@ string CompilerHLSL::compile() return buffer->str(); } + +void CompilerHLSL::emit_block_hints(const SPIRBlock &block) +{ + switch (block.hint) + { + case SPIRBlock::HintFlatten: + statement("[flatten]"); + break; + case SPIRBlock::HintDontFlatten: + statement("[branch]"); + break; + case SPIRBlock::HintUnroll: + statement("[unroll]"); + break; + case SPIRBlock::HintDontUnroll: + statement("[loop]"); + break; + default: + break; + } +} diff --git a/spirv_hlsl.hpp b/spirv_hlsl.hpp index df330d0588..bcc82b1043 100644 --- a/spirv_hlsl.hpp +++ b/spirv_hlsl.hpp @@ -118,9 +118,9 @@ public: private: std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; - std::string image_type_hlsl(const SPIRType &type); - std::string image_type_hlsl_modern(const SPIRType &type); - std::string image_type_hlsl_legacy(const SPIRType &type); + std::string image_type_hlsl(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id); void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; void emit_hlsl_entry_point(); void emit_header() override; @@ -158,6 +158,7 @@ private: void emit_store(const Instruction &instruction); void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); void emit_subgroup_op(const Instruction &i) override; + void emit_block_hints(const SPIRBlock &block) override; void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier, uint32_t base_offset = 0) override; diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 984323b0d8..4f69c57150 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -534,19 +534,44 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: // Add the global variables as arguments to the function if (func_id != entry_point) { - uint32_t next_id = increase_bound_by(uint32_t(added_arg_ids.size())); for (uint32_t arg_id : added_arg_ids) { - auto var = get(arg_id); + auto &var = get(arg_id); uint32_t type_id = var.basetype; - func.add_parameter(type_id, next_id, true); - set(next_id, type_id, StorageClassFunction, 0, arg_id); + auto *p_type = &get(type_id); - // Ensure the existing variable has a valid name and the new variable has all the same meta info - set_name(arg_id, ensure_valid_name(to_name(arg_id), "v")); - meta[next_id] = meta[arg_id]; + if (is_builtin_variable(var) && p_type->basetype == SPIRType::Struct) + { + // Get the non-pointer type + type_id = get_non_pointer_type_id(type_id); + p_type = &get(type_id); - next_id++; + uint32_t mbr_idx = 0; + for (auto &mbr_type_id : p_type->member_types) + { + BuiltIn builtin; + bool is_builtin = is_member_builtin(*p_type, mbr_idx, &builtin); + if (is_builtin && has_active_builtin(builtin, var.storage)) + { + // Add a arg variable with the same type and decorations as the member + uint32_t next_id = increase_bound_by(1); + func.add_parameter(mbr_type_id, next_id, true); + set(next_id, mbr_type_id, StorageClassFunction); + meta[next_id].decoration = meta[type_id].members[mbr_idx]; + } + mbr_idx++; + } + } + else + { + uint32_t next_id = increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + set(next_id, type_id, StorageClassFunction, 0, arg_id); + + // Ensure the existing variable has a valid name and the new variable has all the same meta info + set_name(arg_id, ensure_valid_name(to_name(arg_id), "v")); + meta[next_id] = meta[arg_id]; + } } } } @@ -1148,6 +1173,18 @@ void CompilerMSL::emit_custom_functions() statement(""); break; + case SPVFuncImplTexelBufferCoords: + { + string tex_width_str = convert_to_string(msl_options.texel_buffer_texture_width); + statement("// Returns 2D texture coords corresponding to 1D texel buffer coords"); + statement("uint2 spvTexelBufferCoord(uint tc)"); + begin_scope(); + statement(join("return uint2(tc % ", tex_width_str, ", tc / ", tex_width_str, ");")); + end_scope(); + statement(""); + break; + } + case SPVFuncImplInverse4x4: statement("// Returns the determinant of a 2x2 matrix."); statement("inline float spvDet2x2(float a1, float a2, float b1, float b2)"); @@ -1506,16 +1543,16 @@ void CompilerMSL::emit_specialization_constants() void CompilerMSL::emit_instruction(const Instruction &instruction) { -#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BOP_CAST(op, type) \ +#define MSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_BOP_CAST(op, type) \ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) -#define UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) -#define QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) -#define TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) -#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) -#define BFOP_CAST(op, type) \ +#define MSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define MSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define MSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define MSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_BFOP_CAST(op, type) \ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) -#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define MSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) auto ops = stream(instruction); auto opcode = static_cast(instruction.op); @@ -1527,81 +1564,81 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) case OpIEqual: case OpLogicalEqual: case OpFOrdEqual: - BOP(==); + MSL_BOP(==); break; case OpINotEqual: case OpLogicalNotEqual: case OpFOrdNotEqual: - BOP(!=); + MSL_BOP(!=); break; case OpUGreaterThan: case OpSGreaterThan: case OpFOrdGreaterThan: - BOP(>); + MSL_BOP(>); break; case OpUGreaterThanEqual: case OpSGreaterThanEqual: case OpFOrdGreaterThanEqual: - BOP(>=); + MSL_BOP(>=); break; case OpULessThan: case OpSLessThan: case OpFOrdLessThan: - BOP(<); + MSL_BOP(<); break; case OpULessThanEqual: case OpSLessThanEqual: case OpFOrdLessThanEqual: - BOP(<=); + MSL_BOP(<=); break; // Derivatives case OpDPdx: case OpDPdxFine: case OpDPdxCoarse: - UFOP(dfdx); + MSL_UFOP(dfdx); register_control_dependent_expression(ops[1]); break; case OpDPdy: case OpDPdyFine: case OpDPdyCoarse: - UFOP(dfdy); + MSL_UFOP(dfdy); register_control_dependent_expression(ops[1]); break; case OpFwidth: case OpFwidthCoarse: case OpFwidthFine: - UFOP(fwidth); + MSL_UFOP(fwidth); register_control_dependent_expression(ops[1]); break; // Bitfield case OpBitFieldInsert: - QFOP(insert_bits); + MSL_QFOP(insert_bits); break; case OpBitFieldSExtract: case OpBitFieldUExtract: - TFOP(extract_bits); + MSL_TFOP(extract_bits); break; case OpBitReverse: - UFOP(reverse_bits); + MSL_UFOP(reverse_bits); break; case OpBitCount: - UFOP(popcount); + MSL_UFOP(popcount); break; case OpFRem: - BFOP(fmod); + MSL_BFOP(fmod); break; // Atomics @@ -1654,7 +1691,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) break; } -#define AFMOImpl(op, valsrc) \ +#define MSL_AFMO_IMPL(op, valsrc) \ do \ { \ uint32_t result_type = ops[0]; \ @@ -1665,45 +1702,45 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) emit_atomic_func_op(result_type, id, "atomic_fetch_" #op "_explicit", mem_sem, mem_sem, false, ptr, val); \ } while (false) -#define AFMO(op) AFMOImpl(op, ops[5]) -#define AFMIO(op) AFMOImpl(op, 1) +#define MSL_AFMO(op) MSL_AFMO_IMPL(op, ops[5]) +#define MSL_AFMIO(op) MSL_AFMO_IMPL(op, 1) case OpAtomicIIncrement: - AFMIO(add); + MSL_AFMIO(add); break; case OpAtomicIDecrement: - AFMIO(sub); + MSL_AFMIO(sub); break; case OpAtomicIAdd: - AFMO(add); + MSL_AFMO(add); break; case OpAtomicISub: - AFMO(sub); + MSL_AFMO(sub); break; case OpAtomicSMin: case OpAtomicUMin: - AFMO(min); + MSL_AFMO(min); break; case OpAtomicSMax: case OpAtomicUMax: - AFMO(max); + MSL_AFMO(max); break; case OpAtomicAnd: - AFMO(and); + MSL_AFMO(and); break; case OpAtomicOr: - AFMO(or); + MSL_AFMO(or); break; case OpAtomicXor: - AFMO (xor); + MSL_AFMO (xor); break; // Images @@ -1827,7 +1864,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) break; } -#define ImgQry(qrytype) \ +#define MSL_ImgQry(qrytype) \ do \ { \ uint32_t rslt_type_id = ops[0]; \ @@ -1840,11 +1877,11 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) } while (false) case OpImageQueryLevels: - ImgQry(mip_levels); + MSL_ImgQry(mip_levels); break; case OpImageQuerySamples: - ImgQry(samples); + MSL_ImgQry(samples); break; // Casting @@ -1926,7 +1963,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) e->need_transpose = true; } else - BOP(*); + MSL_BOP(*); break; } @@ -2429,8 +2466,9 @@ string CompilerMSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool if (coord_type.vecsize > 1) tex_coords += ".x"; + // Metal texel buffer textures are 2D, so convert 1D coord to 2D. if (is_fetch) - tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ", 0)"; // Metal textures are 2D + tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; alt_coord = ".y"; @@ -3553,17 +3591,13 @@ std::string CompilerMSL::sampler_type(const SPIRType &type) SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of samplers."); // Arrays of samplers in MSL must be declared with a special array syntax ala C++11 std::array. - auto *parent = &type; - while (parent->pointer) - parent = &get(parent->parent_type); - parent = &get(parent->parent_type); - uint32_t array_size = type.array_size_literal.back() ? type.array.back() : get(type.array.back()).scalar(); - if (array_size == 0) SPIRV_CROSS_THROW("Unsized array of samplers is not supported in MSL."); - return join("array<", sampler_type(*parent), ", ", array_size, ">"); + + auto &parent = get(get_non_pointer_type(type).parent_type); + return join("array<", sampler_type(parent), ", ", array_size, ">"); } else return "sampler"; @@ -3586,25 +3620,20 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id) SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of textures."); // Arrays of images in MSL must be declared with a special array syntax ala C++11 std::array. - auto *parent = &type; - while (parent->pointer) - parent = &get(parent->parent_type); - parent = &get(parent->parent_type); - uint32_t array_size = type.array_size_literal.back() ? type.array.back() : get(type.array.back()).scalar(); if (array_size == 0) SPIRV_CROSS_THROW("Unsized array of images is not supported in MSL."); - return join("array<", image_type_glsl(*parent, id), ", ", array_size, ">"); + + auto &parent = get(get_non_pointer_type(type).parent_type); + return join("array<", image_type_glsl(parent, id), ", ", array_size, ">"); } string img_type_name; // Bypass pointers because we need the real image struct auto &img_type = get(type.self).image; - bool shadow_image = comparison_images.count(id) != 0; - - if (img_type.depth || shadow_image) + if (image_is_comparison(type, id)) { switch (img_type.dim) { @@ -4035,8 +4064,8 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o auto &return_type = compiler.get(args[0]); if (!return_type.array.empty()) return SPVFuncImplArrayCopy; - else - return SPVFuncImplNone; + + break; } case OpStore: @@ -4054,14 +4083,24 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o else { // Or ... an expression. - if (result_types[id_rhs] != 0) - type = &compiler.get(result_types[id_rhs]); + uint32_t tid = result_types[id_rhs]; + if (tid) + type = &compiler.get(tid); } if (type && compiler.is_array(*type)) return SPVFuncImplArrayCopy; - else - return SPVFuncImplNone; + + break; + } + + case OpImageFetch: + { + // Retrieve the image type, and if it's a Buffer, emit a texel coordinate function + uint32_t tid = result_types[args[2]]; + if (tid && compiler.get(tid).image.dim == DimBuffer) + return SPVFuncImplTexelBufferCoords; + break; } @@ -4179,3 +4218,12 @@ void CompilerMSL::remap_constexpr_sampler(uint32_t id, const spirv_cross::MSLCon SPIRV_CROSS_THROW("Can not remap array of samplers."); constexpr_samplers[id] = sampler; } + +// MSL always declares builtins with their SPIR-V type. +void CompilerMSL::bitcast_from_builtin_load(uint32_t, std::string &, const spirv_cross::SPIRType &) +{ +} + +void CompilerMSL::bitcast_to_builtin_store(uint32_t, std::string &, const spirv_cross::SPIRType &) +{ +} diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 61f5eb2995..fe3401d91b 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -152,6 +152,7 @@ public: Platform platform = macOS; uint32_t msl_version = make_msl_version(1, 2); + uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers bool enable_point_size_builtin = true; bool resolve_specialized_array_lengths = true; @@ -215,6 +216,7 @@ public: SPVFuncImplFindSMsb, SPVFuncImplFindUMsb, SPVFuncImplArrayCopy, + SPVFuncImplTexelBufferCoords, SPVFuncImplInverse4x4, SPVFuncImplInverse3x3, SPVFuncImplInverse2x2, @@ -356,6 +358,9 @@ protected: void emit_entry_point_declarations() override; uint32_t builtin_frag_coord_id = 0; + void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; + void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; + Options msl_options; std::set spv_function_implementations; std::unordered_map vtx_attrs_by_location; diff --git a/spirv_reflect.cpp b/spirv_reflect.cpp new file mode 100644 index 0000000000..c322d972bd --- /dev/null +++ b/spirv_reflect.cpp @@ -0,0 +1,573 @@ +/* + * Copyright 2018 Bradley Austin Davis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_reflect.hpp" +#include "spirv_glsl.hpp" +#include + +using namespace spv; +using namespace spirv_cross; +using namespace std; + +namespace simple_json +{ +enum class Type +{ + Object, + Array, +}; + +using State = std::pair; +using Stack = std::stack; + +class Stream +{ + Stack stack; + std::ostringstream buffer; + uint32_t indent{ 0 }; + +public: + void begin_json_object(); + void end_json_object(); + void emit_json_key(const std::string &key); + void emit_json_key_value(const std::string &key, const std::string &value); + void emit_json_key_value(const std::string &key, bool value); + void emit_json_key_value(const std::string &key, uint32_t value); + void emit_json_key_value(const std::string &key, int32_t value); + void emit_json_key_value(const std::string &key, float value); + void emit_json_key_object(const std::string &key); + void emit_json_key_array(const std::string &key); + + void begin_json_array(); + void end_json_array(); + void emit_json_array_value(const std::string &value); + void emit_json_array_value(uint32_t value); + + std::string str() const + { + return buffer.str(); + } + +private: + inline void statement_indent() + { + for (uint32_t i = 0; i < indent; i++) + buffer << " "; + } + + template + inline void statement_inner(T &&t) + { + buffer << std::forward(t); + } + + template + inline void statement_inner(T &&t, Ts &&... ts) + { + buffer << std::forward(t); + statement_inner(std::forward(ts)...); + } + + template + inline void statement(Ts &&... ts) + { + statement_indent(); + statement_inner(std::forward(ts)...); + buffer << '\n'; + } + + template + void statement_no_return(Ts &&... ts) + { + statement_indent(); + statement_inner(std::forward(ts)...); + } +}; +} // namespace simple_json + +using namespace simple_json; + +// Hackery to emit JSON without using nlohmann/json C++ library (which requires a +// higher level of compiler compliance than is required by SPIRV-Cross +void Stream::begin_json_array() +{ + if (!stack.empty() && stack.top().second) + { + statement_inner(",\n"); + } + statement("["); + ++indent; + stack.emplace(Type::Array, false); +} + +void Stream::end_json_array() +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + { + statement_inner("\n"); + } + --indent; + statement_no_return("]"); + stack.pop(); + if (!stack.empty()) + { + stack.top().second = true; + } +} + +void Stream::emit_json_array_value(const std::string &value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + + if (stack.top().second) + statement_inner(",\n"); + + statement_no_return("\"", value, "\""); + stack.top().second = true; +} + +void Stream::emit_json_array_value(uint32_t value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + statement_inner(",\n"); + statement_no_return(std::to_string(value)); + stack.top().second = true; +} + +void Stream::begin_json_object() +{ + if (!stack.empty() && stack.top().second) + { + statement_inner(",\n"); + } + statement("{"); + ++indent; + stack.emplace(Type::Object, false); +} + +void Stream::end_json_object() +{ + if (stack.empty() || stack.top().first != Type::Object) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + { + statement_inner("\n"); + } + --indent; + statement_no_return("}"); + stack.pop(); + if (!stack.empty()) + { + stack.top().second = true; + } +} + +void Stream::emit_json_key(const std::string &key) +{ + if (stack.empty() || stack.top().first != Type::Object) + SPIRV_CROSS_THROW("Invalid JSON state"); + + if (stack.top().second) + statement_inner(",\n"); + statement_no_return("\"", key, "\" : "); + stack.top().second = true; +} + +void Stream::emit_json_key_value(const std::string &key, const std::string &value) +{ + emit_json_key(key); + statement_inner("\"", value, "\""); +} + +void Stream::emit_json_key_value(const std::string &key, uint32_t value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, int32_t value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, float value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, bool value) +{ + emit_json_key(key); + statement_inner(value ? "true" : "false"); +} + +void Stream::emit_json_key_object(const std::string &key) +{ + emit_json_key(key); + statement_inner("{\n"); + ++indent; + stack.emplace(Type::Object, false); +} + +void Stream::emit_json_key_array(const std::string &key) +{ + emit_json_key(key); + statement_inner("[\n"); + ++indent; + stack.emplace(Type::Array, false); +} + +void CompilerReflection::set_format(const std::string &format) +{ + if (format != "json") + { + SPIRV_CROSS_THROW("Unsupported format"); + } +} + +string CompilerReflection::compile() +{ + // Force a classic "C" locale, reverts when function returns + ClassicLocale classic_locale; + + // Move constructor for this type is broken on GCC 4.9 ... + json_stream = std::make_shared(); + json_stream->begin_json_object(); + emit_entry_points(); + emit_types(); + emit_resources(); + emit_specialization_constants(); + json_stream->end_json_object(); + return json_stream->str(); +} + +void CompilerReflection::emit_types() +{ + bool emitted_open_tag = false; + for (auto &id : ids) + { + auto idType = id.get_type(); + if (idType == TypeType) + { + auto &type = id.get(); + if (type.basetype == SPIRType::Struct && !type.pointer && type.array.empty()) + { + emit_type(type, emitted_open_tag); + } + } + } + + if (emitted_open_tag) + { + json_stream->end_json_object(); + } +} + +void CompilerReflection::emit_type(const SPIRType &type, bool &emitted_open_tag) +{ + auto name = type_to_glsl(type); + + if (type.type_alias != 0) + return; + + if (!emitted_open_tag) + { + json_stream->emit_json_key_object("types"); + emitted_open_tag = true; + } + json_stream->emit_json_key_object("_" + std::to_string(type.self)); + json_stream->emit_json_key_value("name", name); + json_stream->emit_json_key_array("members"); + // FIXME ideally we'd like to emit the size of a structure as a + // convenience to people parsing the reflected JSON. The problem + // is that there's no implicit size for a type. It's final size + // will be determined by the top level declaration in which it's + // included. So there might be one size for the struct if it's + // included in a std140 uniform block and another if it's included + // in a std430 uniform block. + // The solution is to include *all* potential sizes as a map of + // layout type name to integer, but that will probably require + // some additional logic being written in this class, or in the + // parent CompilerGLSL class. + auto size = type.member_types.size(); + for (uint32_t i = 0; i < size; ++i) + { + emit_type_member(type, i); + } + json_stream->end_json_array(); + json_stream->end_json_object(); +} + +void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index) +{ + auto &membertype = get(type.member_types[index]); + json_stream->begin_json_object(); + auto name = to_member_name(type, index); + // FIXME we'd like to emit the offset of each member, but such offsets are + // context dependent. See the comment above regarding structure sizes + json_stream->emit_json_key_value("name", name); + if (membertype.basetype == SPIRType::Struct) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self)); + } + else + { + json_stream->emit_json_key_value("type", type_to_glsl(membertype)); + } + emit_type_member_qualifiers(type, index); + json_stream->end_json_object(); +} + +void CompilerReflection::emit_type_array(const SPIRType &type) +{ + if (!type.array.empty()) + { + json_stream->emit_json_key_array("array"); + // Note that we emit the zeros here as a means of identifying + // unbounded arrays. This is necessary as otherwise there would + // be no way of differentiating between float[4] and float[4][] + for (const auto &value : type.array) + json_stream->emit_json_array_value(value); + json_stream->end_json_array(); + } +} + +void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index) +{ + auto flags = combined_decoration_for_member(type, index); + if (flags.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + + auto &membertype = get(type.member_types[index]); + emit_type_array(membertype); + auto &memb = meta[type.self].members; + if (index < memb.size()) + { + auto &dec = memb[index]; + if (dec.decoration_flags.get(DecorationLocation)) + json_stream->emit_json_key_value("location", dec.location); + if (dec.decoration_flags.get(DecorationOffset)) + json_stream->emit_json_key_value("offset", dec.offset); + } +} + +string CompilerReflection::execution_model_to_str(spv::ExecutionModel model) +{ + switch (model) + { + case spv::ExecutionModelVertex: + return "vert"; + case spv::ExecutionModelTessellationControl: + return "tesc"; + case ExecutionModelTessellationEvaluation: + return "tese"; + case ExecutionModelGeometry: + return "geom"; + case ExecutionModelFragment: + return "frag"; + case ExecutionModelGLCompute: + return "comp"; + default: + return "???"; + } +} + +// FIXME include things like the local_size dimensions, geometry output vertex count, etc +void CompilerReflection::emit_entry_points() +{ + auto entries = get_entry_points_and_stages(); + if (!entries.empty()) + { + json_stream->emit_json_key_array("entryPoints"); + for (auto &e : entries) + { + json_stream->begin_json_object(); + json_stream->emit_json_key_value("name", e.name); + json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model)); + json_stream->end_json_object(); + } + json_stream->end_json_array(); + } +} + +void CompilerReflection::emit_resources() +{ + auto res = get_shader_resources(); + emit_resources("subpass_inputs", res.subpass_inputs); + emit_resources("inputs", res.stage_inputs); + emit_resources("outputs", res.stage_outputs); + emit_resources("textures", res.sampled_images); + emit_resources("separate_images", res.separate_images); + emit_resources("separate_samplers", res.separate_samplers); + emit_resources("images", res.storage_images); + emit_resources("ssbos", res.storage_buffers); + emit_resources("ubos", res.uniform_buffers); + emit_resources("push_constants", res.push_constant_buffers); + emit_resources("counters", res.atomic_counters); +} + +void CompilerReflection::emit_resources(const char *tag, const vector &resources) +{ + if (resources.empty()) + { + return; + } + + json_stream->emit_json_key_array(tag); + for (auto &res : resources) + { + auto &type = get_type(res.type_id); + auto typeflags = meta[type.self].decoration.decoration_flags; + auto &mask = get_decoration_bitset(res.id); + + // If we don't have a name, use the fallback for the type instead of the variable + // for SSBOs and UBOs since those are the only meaningful names to use externally. + // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks. + bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant; + bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) || + get_decoration_bitset(type.self).get(DecorationBufferBlock); + + uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id; + + json_stream->begin_json_object(); + + if (type.basetype == SPIRType::Struct) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id)); + } + else + { + json_stream->emit_json_key_value("type", type_to_glsl(type)); + } + + json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id)); + { + bool ssbo_block = type.storage == StorageClassStorageBuffer || + (type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock)); + if (ssbo_block) + { + auto buffer_flags = get_buffer_block_flags(res.id); + if (buffer_flags.get(DecorationNonReadable)) + json_stream->emit_json_key_value("writeonly", true); + if (buffer_flags.get(DecorationNonWritable)) + json_stream->emit_json_key_value("readonly", true); + if (buffer_flags.get(DecorationRestrict)) + json_stream->emit_json_key_value("restrict", true); + if (buffer_flags.get(DecorationCoherent)) + json_stream->emit_json_key_value("coherent", true); + } + } + + emit_type_array(type); + + { + bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform || + get_storage_class(res.id) == StorageClassUniformConstant); + if (is_sized_block) + { + uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id))); + json_stream->emit_json_key_value("block_size", block_size); + } + } + + if (type.storage == StorageClassPushConstant) + json_stream->emit_json_key_value("push_constant", true); + if (mask.get(DecorationLocation)) + json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation)); + if (mask.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + if (mask.get(DecorationColMajor)) + json_stream->emit_json_key_value("column_major", true); + if (mask.get(DecorationIndex)) + json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex)); + if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet)) + json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet)); + if (mask.get(DecorationBinding)) + json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding)); + if (mask.get(DecorationInputAttachmentIndex)) + json_stream->emit_json_key_value("input_attachment_index", + get_decoration(res.id, DecorationInputAttachmentIndex)); + if (mask.get(DecorationOffset)) + json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset)); + + // For images, the type itself adds a layout qualifer. + // Only emit the format for storage images. + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + { + const char *fmt = format_to_glsl(type.image.format); + if (fmt != nullptr) + json_stream->emit_json_key_value("format", std::string(fmt)); + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); +} + +void CompilerReflection::emit_specialization_constants() +{ + auto specialization_constants = get_specialization_constants(); + if (specialization_constants.empty()) + return; + + json_stream->emit_json_key_array("specialization_constants"); + for (const auto spec_const : specialization_constants) + { + auto &c = get(spec_const.id); + auto type = get(c.constant_type); + json_stream->begin_json_object(); + json_stream->emit_json_key_value("id", spec_const.constant_id); + json_stream->emit_json_key_value("type", type_to_glsl(type)); + switch (type.basetype) + { + case SPIRType::UInt: + json_stream->emit_json_key_value("default_value", c.scalar()); + break; + + case SPIRType::Int: + json_stream->emit_json_key_value("default_value", c.scalar_i32()); + break; + + case SPIRType::Float: + json_stream->emit_json_key_value("default_value", c.scalar_f32()); + break; + + case SPIRType::Boolean: + json_stream->emit_json_key_value("default_value", c.scalar() != 0); + break; + + default: + break; + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); +} + +string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const +{ + auto &memb = meta[type.self].members; + if (index < memb.size() && !memb[index].alias.empty()) + return memb[index].alias; + else + return join("_m", index); +} diff --git a/spirv_reflect.hpp b/spirv_reflect.hpp new file mode 100644 index 0000000000..e402fa8fb9 --- /dev/null +++ b/spirv_reflect.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 2018 Bradley Austin Davis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_REFLECT_HPP +#define SPIRV_CROSS_REFLECT_HPP + +#include "spirv_glsl.hpp" +#include +#include + +namespace simple_json +{ +class Stream; +} + +namespace spirv_cross +{ +class CompilerReflection : public CompilerGLSL +{ + using Parent = CompilerGLSL; + +public: + CompilerReflection(std::vector spirv_) + : Parent(move(spirv_)) + { + options.vulkan_semantics = true; + } + + CompilerReflection(const uint32_t *ir, size_t word_count) + : Parent(ir, word_count) + { + options.vulkan_semantics = true; + } + + void set_format(const std::string &format); + std::string compile() override; + +private: + static std::string execution_model_to_str(spv::ExecutionModel model); + + void emit_entry_points(); + void emit_types(); + void emit_resources(); + void emit_specialization_constants(); + + void emit_type(const SPIRType &type, bool &emitted_open_tag); + void emit_type_member(const SPIRType &type, uint32_t index); + void emit_type_member_qualifiers(const SPIRType &type, uint32_t index); + void emit_type_array(const SPIRType &type); + void emit_resources(const char *tag, const std::vector &resources); + + std::string to_member_name(const SPIRType &type, uint32_t index) const; + + std::shared_ptr json_stream; +}; + +} // namespace spirv_cross + +#endif diff --git a/test_shaders.py b/test_shaders.py index 3efcf923d1..40e1c2e2c3 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -11,8 +11,13 @@ import hashlib import shutil import argparse import codecs +import json +import multiprocessing +import errno +from functools import partial -force_no_external_validation = False +backend = 'glsl' +args = {} def remove_file(path): #print('Removing file:', path) @@ -78,7 +83,7 @@ def print_msl_compiler_version(): subprocess.check_call(['xcrun', '--sdk', 'iphoneos', 'metal', '--version']) print('...are the Metal compiler characteristics.\n') # display after so xcrun FNF is silent except OSError as e: - if (e.errno != os.errno.ENOENT): # Ignore xcrun not found error + if (e.errno != errno.ENOENT): # Ignore xcrun not found error raise def validate_shader_msl(shader, opt): @@ -90,7 +95,7 @@ def validate_shader_msl(shader, opt): subprocess.check_call(['xcrun', '--sdk', msl_os, 'metal', '-x', 'metal', '-std=osx-metal{}'.format('2.0' if msl2 else '1.2'), '-Werror', '-Wno-unused-variable', msl_path]) print('Compiled Metal shader: ' + msl_path) # display after so xcrun FNF is silent except OSError as oe: - if (oe.errno != os.errno.ENOENT): # Ignore xcrun not found error + if (oe.errno != errno.ENOENT): # Ignore xcrun not found error raise except subprocess.CalledProcessError: print('Error compiling Metal shader: ' + msl_path) @@ -140,7 +145,7 @@ def shader_to_win_path(shader): stdout_data, stderr_data = f.communicate() return stdout_data.decode('utf-8') except OSError as oe: - if (oe.errno != os.errno.ENOENT): # Ignore not found errors + if (oe.errno != errno.ENOENT): # Ignore not found errors return shader except subprocess.CalledProcessError: raise @@ -152,12 +157,12 @@ def validate_shader_hlsl(shader): subprocess.check_call(['glslangValidator', '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader]) is_no_fxc = '.nofxc.' in shader global ignore_fxc - if (not ignore_fxc) and (not force_no_external_validation) and (not is_no_fxc): + if (not ignore_fxc) and (not args.force_no_external_validation) and (not is_no_fxc): try: win_path = shader_to_win_path(shader) subprocess.check_call(['fxc', '-nologo', shader_model_hlsl(shader), win_path]) except OSError as oe: - if (oe.errno != os.errno.ENOENT): # Ignore not found errors + if (oe.errno != errno.ENOENT): # Ignore not found errors raise else: ignore_fxc = True @@ -199,6 +204,24 @@ def cross_compile_hlsl(shader, spirv, opt): return (spirv_path, hlsl_path) +def cross_compile_reflect(shader, spirv, opt): + spirv_path = create_temporary() + reflect_path = create_temporary(os.path.basename(shader)) + + if spirv: + subprocess.check_call(['spirv-as', '-o', spirv_path, shader]) + else: + subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + + if opt: + subprocess.check_call(['spirv-opt', '-O', '-o', spirv_path, spirv_path]) + + spirv_cross_path = './spirv-cross' + + sm = shader_to_sm(shader) + subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect']) + return (spirv_path, reflect_path) + def validate_shader(shader, vulkan): if vulkan: subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', shader]) @@ -277,6 +300,58 @@ def reference_path(directory, relpath, opt): reference_dir = os.path.join(reference_dir, split_paths[1]) return os.path.join(reference_dir, relpath) +def json_ordered(obj): + if isinstance(obj, dict): + return sorted((k, json_ordered(v)) for k, v in obj.items()) + if isinstance(obj, list): + return sorted(json_ordered(x) for x in obj) + else: + return obj + +def json_compare(json_a, json_b): + return json_ordered(json_a) == json_ordered(json_b) + +def regression_check_reflect(shader, json_file, update, keep, opt): + reference = reference_path(shader[0], shader[1], opt) + '.json' + joined_path = os.path.join(shader[0], shader[1]) + print('Reference shader reflection path:', reference) + if os.path.exists(reference): + actual = '' + expected = '' + with open(json_file) as f: + actual_json = f.read(); + actual = json.loads(actual_json) + with open(reference) as f: + expected = json.load(f) + if (json_compare(actual, expected) != True): + if update: + print('Generated reflection json has changed for {}!'.format(reference)) + # If we expect changes, update the reference file. + if os.path.exists(reference): + remove_file(reference) + make_reference_dir(reference) + shutil.move(json_file, reference) + else: + print('Generated reflection json in {} does not match reference {}!'.format(json_file, reference)) + with open(json_file, 'r') as f: + print('') + print('Generated:') + print('======================') + print(f.read()) + print('======================') + print('') + + # Otherwise, fail the test. Keep the shader file around so we can inspect. + if not keep: + remove_file(json_file) + sys.exit(1) + else: + remove_file(json_file) + else: + print('Found new shader {}. Placing generated source code in {}'.format(joined_path, reference)) + make_reference_dir(reference) + shutil.move(json_file, reference) + def regression_check(shader, glsl, update, keep, opt): reference = reference_path(shader[0], shader[1], opt) joined_path = os.path.join(shader[0], shader[1]) @@ -396,7 +471,7 @@ def test_shader_msl(stats, shader, update, keep, opt): # executable from Xcode using args: `--msl --entry main --output msl_path spirv_path`. # print('SPRIV shader: ' + spirv) - if not force_no_external_validation: + if not args.force_no_external_validation: validate_shader_msl(shader, opt) remove_file(spirv) @@ -410,26 +485,50 @@ def test_shader_hlsl(stats, shader, update, keep, opt): regression_check(shader, hlsl, update, keep, opt) remove_file(spirv) -def test_shaders_helper(stats, shader_dir, update, malisc, keep, opt, backend): - for root, dirs, files in os.walk(os.path.join(shader_dir)): +def test_shader_reflect(stats, shader, update, keep, opt): + joined_path = os.path.join(shader[0], shader[1]) + print('Testing shader reflection:', joined_path) + is_spirv = shader_is_spirv(shader[1]) + noopt = shader_is_noopt(shader[1]) + spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt)) + regression_check_reflect(shader, reflect, update, keep, opt) + remove_file(spirv) + +def test_shader_file(relpath, stats, shader_dir, update, keep, opt, backend): + if backend == 'msl': + test_shader_msl(stats, (shader_dir, relpath), update, keep, opt) + elif backend == 'hlsl': + test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt) + elif backend == 'reflect': + test_shader_reflect(stats, (shader_dir, relpath), update, keep, opt) + else: + test_shader(stats, (shader_dir, relpath), update, keep, opt) + +def test_shaders_helper(stats): + all_files = [] + for root, dirs, files in os.walk(os.path.join(args.folder)): files = [ f for f in files if not f.startswith(".") ] #ignore system files (esp OSX) for i in files: path = os.path.join(root, i) - relpath = os.path.relpath(path, shader_dir) - if backend == 'msl': - test_shader_msl(stats, (shader_dir, relpath), update, keep, opt) - elif backend == 'hlsl': - test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt) - else: - test_shader(stats, (shader_dir, relpath), update, keep, opt) + relpath = os.path.relpath(path, args.folder) + all_files.append(relpath) -def test_shaders(shader_dir, update, malisc, keep, opt, backend): - if malisc: + # The child processes in parallel execution mode don't have the proper state for the global args variable, so + # at this point we need to switch to explicit arguments + if args.parallel: + pool = multiprocessing.Pool(multiprocessing.cpu_count()) + pool.map(partial(test_shader_file, stats=stats, shader_dir=args.folder, update=args.update, keep=args.keep, opt=args.opt, backend=backend), all_files) + else: + for i in all_files: + test_shader_file(i, stats, args.folder, args.update, args.keep, args.opt, backend) + +def test_shaders(): + if args.malisc: with open('stats.csv', 'w') as stats: print('Shader,OrigRegs,OrigUniRegs,OrigALUShort,OrigLSShort,OrigTEXShort,OrigALULong,OrigLSLong,OrigTEXLong,CrossRegs,CrossUniRegs,CrossALUShort,CrossLSShort,CrossTEXShort,CrossALULong,CrossLSLong,CrossTEXLong', file = stats) - test_shaders_helper(stats, shader_dir, update, malisc, keep, backend) + test_shaders_helper(stats) else: - test_shaders_helper(None, shader_dir, update, malisc, keep, opt, backend) + test_shaders_helper(None) def main(): parser = argparse.ArgumentParser(description = 'Script for regression testing.') @@ -459,19 +558,35 @@ def main(): parser.add_argument('--opt', action = 'store_true', help = 'Run SPIRV-Tools optimization passes as well.') + parser.add_argument('--reflect', + action = 'store_true', + help = 'Test reflection backend.') + parser.add_argument('--parallel', + action = 'store_true', + help = 'Execute tests in parallel. Useful for doing regression quickly, but bad for debugging and stat output.') + + global args args = parser.parse_args() - if not args.folder: sys.stderr.write('Need shader folder.\n') sys.exit(1) + if (args.parallel and (args.malisc or args.force_no_external_validation or args.update)): + sys.stderr.write('Parallel execution is disabled when using the flags --update, --malisc or --force-no-external-validation\n') + args.parallel = False + if args.msl: print_msl_compiler_version() - global force_no_external_validation - force_no_external_validation = args.force_no_external_validation + global backend + if (args.msl or args.metal): + backend = 'msl' + elif args.hlsl: + backend = 'hlsl' + elif args.reflect: + backend = 'reflect' - test_shaders(args.folder, args.update, args.malisc, args.keep, args.opt, 'msl' if (args.msl or args.metal) else ('hlsl' if args.hlsl else 'glsl')) + test_shaders() if args.malisc: print('Stats in stats.csv!') print('Tests completed!') diff --git a/test_shaders.sh b/test_shaders.sh index e96978a1e6..8a43afb4df 100755 --- a/test_shaders.sh +++ b/test_shaders.sh @@ -16,4 +16,5 @@ echo "Using spirv-opt in: $(which spirv-opt)." ./test_shaders.py shaders-hlsl --hlsl || exit 1 ./test_shaders.py shaders-hlsl --hlsl --opt || exit 1 ./test_shaders.py shaders-hlsl-no-opt --hlsl || exit 1 +./test_shaders.py shaders-reflection --reflect || exit 1 diff --git a/update_test_shaders.sh b/update_test_shaders.sh index 4bc87a1564..1582c6c3f3 100755 --- a/update_test_shaders.sh +++ b/update_test_shaders.sh @@ -16,5 +16,6 @@ echo "Using spirv-opt in: $(which spirv-opt)." ./test_shaders.py shaders-hlsl --update --hlsl || exit 1 ./test_shaders.py shaders-hlsl --update --hlsl --opt || exit 1 ./test_shaders.py shaders-hlsl-no-opt --update --hlsl || exit 1 +./test_shaders.py shaders-reflection --reflect --update || exit 1